summaryrefslogtreecommitdiffstats
path: root/scripting/source
diff options
context:
space:
mode:
Diffstat (limited to 'scripting/source')
-rw-r--r--scripting/source/basprov/baslibnode.cxx129
-rw-r--r--scripting/source/basprov/baslibnode.hxx70
-rw-r--r--scripting/source/basprov/basmethnode.cxx292
-rw-r--r--scripting/source/basprov/basmethnode.hxx108
-rw-r--r--scripting/source/basprov/basmodnode.cxx135
-rw-r--r--scripting/source/basprov/basmodnode.hxx64
-rw-r--r--scripting/source/basprov/basprov.component29
-rw-r--r--scripting/source/basprov/basprov.cxx476
-rw-r--r--scripting/source/basprov/basprov.hxx97
-rw-r--r--scripting/source/basprov/basscript.cxx315
-rw-r--r--scripting/source/basprov/basscript.hxx103
-rw-r--r--scripting/source/dlgprov/DialogModelProvider.cxx166
-rw-r--r--scripting/source/dlgprov/DialogModelProvider.hxx87
-rw-r--r--scripting/source/dlgprov/dlgevtatt.cxx659
-rw-r--r--scripting/source/dlgprov/dlgevtatt.hxx131
-rw-r--r--scripting/source/dlgprov/dlgprov.component32
-rw-r--r--scripting/source/dlgprov/dlgprov.cxx702
-rw-r--r--scripting/source/dlgprov/dlgprov.hxx148
-rw-r--r--scripting/source/inc/bcholder.hxx44
-rw-r--r--scripting/source/inc/util/MiscUtils.hxx138
-rw-r--r--scripting/source/protocolhandler/protocolhandler.component26
-rw-r--r--scripting/source/protocolhandler/scripthandler.cxx436
-rw-r--r--scripting/source/protocolhandler/scripthandler.hxx114
-rw-r--r--scripting/source/provider/ActiveMSPList.cxx295
-rw-r--r--scripting/source/provider/ActiveMSPList.hxx94
-rw-r--r--scripting/source/provider/BrowseNodeFactoryImpl.cxx650
-rw-r--r--scripting/source/provider/BrowseNodeFactoryImpl.hxx70
-rw-r--r--scripting/source/provider/MasterScriptProvider.cxx676
-rw-r--r--scripting/source/provider/MasterScriptProvider.hxx131
-rw-r--r--scripting/source/provider/MasterScriptProviderFactory.cxx85
-rw-r--r--scripting/source/provider/MasterScriptProviderFactory.hxx74
-rw-r--r--scripting/source/provider/ProviderCache.cxx203
-rw-r--r--scripting/source/provider/ProviderCache.hxx80
-rw-r--r--scripting/source/provider/URIHelper.cxx256
-rw-r--r--scripting/source/provider/URIHelper.hxx87
-rw-r--r--scripting/source/pyprov/mailmerge.README18
-rw-r--r--scripting/source/pyprov/mailmerge.component28
-rw-r--r--scripting/source/pyprov/mailmerge.py533
-rw-r--r--scripting/source/pyprov/msgbox.py241
-rw-r--r--scripting/source/pyprov/pythonscript.py1146
-rw-r--r--scripting/source/pyprov/scriptproviderforpython.rdb28
-rw-r--r--scripting/source/stringresource/stringresource.component34
-rw-r--r--scripting/source/stringresource/stringresource.cxx2584
-rw-r--r--scripting/source/stringresource/stringresource.hxx486
-rw-r--r--scripting/source/vbaevents/eventhelper.cxx983
-rw-r--r--scripting/source/vbaevents/vbaevents.component30
46 files changed, 13313 insertions, 0 deletions
diff --git a/scripting/source/basprov/baslibnode.cxx b/scripting/source/basprov/baslibnode.cxx
new file mode 100644
index 0000000000..566883757b
--- /dev/null
+++ b/scripting/source/basprov/baslibnode.cxx
@@ -0,0 +1,129 @@
+/* -*- 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 "baslibnode.hxx"
+#include "basmodnode.hxx"
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <basic/basmgr.hxx>
+#include <basic/sbstar.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+
+
+namespace basprov
+{
+
+
+ // BasicLibraryNodeImpl
+
+
+ BasicLibraryNodeImpl::BasicLibraryNodeImpl( const Reference< XComponentContext >& rxContext,
+ OUString sScriptingContext, BasicManager* pBasicManager,
+ const Reference< script::XLibraryContainer >& xLibContainer, OUString sLibName, bool isAppScript )
+ :m_xContext( rxContext )
+ ,m_sScriptingContext(std::move( sScriptingContext ))
+ ,m_pBasicManager( pBasicManager )
+ ,m_xLibContainer( xLibContainer )
+ ,m_sLibName(std::move( sLibName ))
+ ,m_bIsAppScript( isAppScript )
+ {
+ if ( m_xLibContainer.is() )
+ {
+ Any aElement = m_xLibContainer->getByName( m_sLibName );
+ aElement >>= m_xLibrary;
+ }
+ }
+
+
+ BasicLibraryNodeImpl::~BasicLibraryNodeImpl()
+ {
+ }
+
+
+ // XBrowseNode
+
+
+ OUString BasicLibraryNodeImpl::getName( )
+ {
+ return m_sLibName;
+ }
+
+
+ Sequence< Reference< browse::XBrowseNode > > BasicLibraryNodeImpl::getChildNodes( )
+ {
+ SolarMutexGuard aGuard;
+
+ std::vector< Reference< browse::XBrowseNode > > aChildNodes;
+
+ if ( m_xLibContainer.is() && m_xLibContainer->hasByName( m_sLibName ) && !m_xLibContainer->isLibraryLoaded( m_sLibName ) )
+ m_xLibContainer->loadLibrary( m_sLibName );
+
+ if ( m_pBasicManager )
+ {
+ StarBASIC* pBasic = m_pBasicManager->GetLib( m_sLibName );
+ if ( pBasic && m_xLibrary.is() )
+ {
+ Sequence< OUString > aNames = m_xLibrary->getElementNames();
+ sal_Int32 nCount = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ aChildNodes.resize( nCount );
+
+ for ( sal_Int32 i = 0 ; i < nCount ; ++i )
+ {
+ SbModule* pModule = pBasic->FindModule( pNames[i] );
+ if ( pModule )
+ aChildNodes[i] = new BasicModuleNodeImpl(m_xContext, m_sScriptingContext,
+ pModule, m_bIsAppScript);
+ }
+ }
+ }
+
+ return comphelper::containerToSequence(aChildNodes);
+ }
+
+
+ sal_Bool BasicLibraryNodeImpl::hasChildNodes( )
+ {
+ SolarMutexGuard aGuard;
+
+ bool bReturn = false;
+ if ( m_xLibrary.is() )
+ bReturn = m_xLibrary->hasElements();
+
+ return bReturn;
+ }
+
+
+ sal_Int16 BasicLibraryNodeImpl::getType( )
+ {
+ return browse::BrowseNodeTypes::CONTAINER;
+ }
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/baslibnode.hxx b/scripting/source/basprov/baslibnode.hxx
new file mode 100644
index 0000000000..929ad05b93
--- /dev/null
+++ b/scripting/source/basprov/baslibnode.hxx
@@ -0,0 +1,70 @@
+/* -*- 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/script/XLibraryContainer.hpp>
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+
+class BasicManager;
+
+
+namespace basprov
+{
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::browse::XBrowseNode > BasicLibraryNodeImpl_BASE;
+
+
+ class BasicLibraryNodeImpl : public BasicLibraryNodeImpl_BASE
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ OUString m_sScriptingContext;
+ BasicManager* m_pBasicManager;
+ css::uno::Reference< css::script::XLibraryContainer > m_xLibContainer;
+ css::uno::Reference< css::container::XNameContainer > m_xLibrary;
+ OUString m_sLibName;
+ bool m_bIsAppScript;
+
+ public:
+ BasicLibraryNodeImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ OUString sScriptingContext,
+ BasicManager* pBasicManager,
+ const css::uno::Reference< css::script::XLibraryContainer >& xLibContainer,
+ OUString sLibName, bool isAppScript );
+ virtual ~BasicLibraryNodeImpl() override;
+
+ // XBrowseNode
+ virtual OUString SAL_CALL getName( ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override;
+ virtual sal_Bool SAL_CALL hasChildNodes( ) override;
+ virtual sal_Int16 SAL_CALL getType( ) override;
+ };
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basmethnode.cxx b/scripting/source/basprov/basmethnode.cxx
new file mode 100644
index 0000000000..f26e4c9a93
--- /dev/null
+++ b/scripting/source/basprov/basmethnode.cxx
@@ -0,0 +1,292 @@
+/* -*- 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 "basmethnode.hxx"
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/frame/DispatchHelper.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbmod.hxx>
+
+#include <util/MiscUtils.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::comphelper;
+using namespace ::com::sun::star::script;
+using namespace ::sf_misc;
+
+#define BASPROV_PROPERTY_ID_URI 1
+#define BASPROV_PROPERTY_ID_EDITABLE 2
+
+constexpr OUStringLiteral BASPROV_PROPERTY_URI = u"URI";
+constexpr OUString BASPROV_PROPERTY_EDITABLE = u"Editable"_ustr;
+
+#define BASPROV_DEFAULT_ATTRIBS() PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY
+
+
+namespace basprov
+{
+
+
+ // BasicMethodNodeImpl
+
+
+ BasicMethodNodeImpl::BasicMethodNodeImpl( const Reference< XComponentContext >& rxContext,
+ OUString sScriptingContext, SbMethod* pMethod, bool isAppScript )
+ : ::scripting_helper::OBroadcastHelperHolder( m_aMutex )
+ ,OPropertyContainer( GetBroadcastHelper() )
+ ,m_xContext( rxContext )
+ ,m_sScriptingContext(std::move( sScriptingContext ))
+ ,m_pMethod( pMethod )
+ ,m_bIsAppScript( isAppScript )
+ ,m_bEditable( true )
+ {
+ if ( m_pMethod )
+ {
+ SbModule* pModule = m_pMethod->GetModule();
+ if ( pModule )
+ {
+ StarBASIC* pBasic = static_cast< StarBASIC* >( pModule->GetParent() );
+ if ( pBasic )
+ {
+ m_sURI = "vnd.sun.star.script:";
+ m_sURI += pBasic->GetName();
+ m_sURI += ".";
+ m_sURI += pModule->GetName();
+ m_sURI += ".";
+ m_sURI += m_pMethod->GetName();
+ m_sURI += "?language=Basic&location=";
+ if ( m_bIsAppScript )
+ m_sURI += "application";
+ else
+ m_sURI += "document";
+ }
+ }
+ }
+
+ registerProperty( BASPROV_PROPERTY_URI, BASPROV_PROPERTY_ID_URI, BASPROV_DEFAULT_ATTRIBS(), &m_sURI, cppu::UnoType<decltype(m_sURI)>::get() );
+ registerProperty( BASPROV_PROPERTY_EDITABLE, BASPROV_PROPERTY_ID_EDITABLE, BASPROV_DEFAULT_ATTRIBS(), &m_bEditable, cppu::UnoType<decltype(m_bEditable)>::get() );
+ }
+
+
+ BasicMethodNodeImpl::~BasicMethodNodeImpl()
+ {
+ }
+
+
+ // XInterface
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( BasicMethodNodeImpl, BasicMethodNodeImpl_BASE, OPropertyContainer )
+
+
+ // XTypeProvider
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( BasicMethodNodeImpl, BasicMethodNodeImpl_BASE, OPropertyContainer )
+
+
+ // XBrowseNode
+
+
+ OUString BasicMethodNodeImpl::getName( )
+ {
+ SolarMutexGuard aGuard;
+
+ OUString sMethodName;
+ if ( m_pMethod )
+ sMethodName = m_pMethod->GetName();
+
+ return sMethodName;
+ }
+
+
+ Sequence< Reference< browse::XBrowseNode > > BasicMethodNodeImpl::getChildNodes( )
+ {
+ return Sequence< Reference< browse::XBrowseNode > >();
+ }
+
+
+ sal_Bool BasicMethodNodeImpl::hasChildNodes( )
+ {
+ return false;
+ }
+
+
+ sal_Int16 BasicMethodNodeImpl::getType( )
+ {
+ return browse::BrowseNodeTypes::SCRIPT;
+ }
+
+
+ // OPropertySetHelper
+
+
+ ::cppu::IPropertyArrayHelper& BasicMethodNodeImpl::getInfoHelper( )
+ {
+ return *getArrayHelper();
+ }
+
+
+ // OPropertyArrayUsageHelper
+
+
+ ::cppu::IPropertyArrayHelper* BasicMethodNodeImpl::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+ }
+
+
+ // XPropertySet
+
+
+ Reference< XPropertySetInfo > BasicMethodNodeImpl::getPropertySetInfo( )
+ {
+ Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+
+ // XInvocation
+
+
+ Reference< XIntrospectionAccess > BasicMethodNodeImpl::getIntrospection( )
+ {
+ return Reference< XIntrospectionAccess >();
+ }
+
+
+ Any BasicMethodNodeImpl::invoke( const OUString& aFunctionName, const Sequence< Any >&,
+ Sequence< sal_Int16 >&, Sequence< Any >& )
+ {
+ if ( aFunctionName != BASPROV_PROPERTY_EDITABLE )
+ {
+ throw IllegalArgumentException(
+ "BasicMethodNodeImpl::invoke: function name not supported!",
+ Reference< XInterface >(), 1 );
+ }
+
+ OUString sDocURL, sLibName, sModName;
+ sal_uInt16 nLine1 = 0;
+
+ if ( !m_bIsAppScript )
+ {
+ Reference< frame::XModel > xModel = MiscUtils::tDocUrlToModel( m_sScriptingContext );
+
+ if ( xModel.is() )
+ {
+ sDocURL = xModel->getURL();
+ if ( sDocURL.isEmpty() )
+ {
+ const Sequence < PropertyValue > aProps = xModel->getArgs();
+ // TODO: according to MBA the property 'Title' may change in future
+ const PropertyValue* pProp = std::find_if(aProps.begin(), aProps.end(),
+ [](const PropertyValue& rProp) { return rProp.Name == "Title"; });
+ if (pProp != aProps.end())
+ pProp->Value >>= sDocURL;
+ }
+ }
+ }
+
+ if ( m_pMethod )
+ {
+ sal_uInt16 nLine2;
+ m_pMethod->GetLineRange( nLine1, nLine2 );
+ SbModule* pModule = m_pMethod->GetModule();
+ if ( pModule )
+ {
+ sModName = pModule->GetName();
+ StarBASIC* pBasic = static_cast< StarBASIC* >( pModule->GetParent() );
+ if ( pBasic )
+ sLibName = pBasic->GetName();
+ }
+ }
+
+ if ( m_xContext.is() )
+ {
+ Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( m_xContext );
+
+ Reference < frame::XDispatchProvider > xProv( xDesktop->getCurrentFrame(), UNO_QUERY );
+
+ if ( xProv.is() )
+ {
+ Reference< frame::XDispatchHelper > xHelper( frame::DispatchHelper::create( m_xContext ) );
+
+ Sequence < PropertyValue > aArgs{
+ comphelper::makePropertyValue("Document", sDocURL),
+ comphelper::makePropertyValue("LibName", sLibName),
+ comphelper::makePropertyValue("Name", sModName),
+ comphelper::makePropertyValue("Type", OUString("Module")),
+ comphelper::makePropertyValue("Line", static_cast< sal_uInt32 >( nLine1 ))
+ };
+ xHelper->executeDispatch( xProv, ".uno:BasicIDEAppear", OUString(), 0, aArgs );
+ }
+ }
+
+
+ return Any();
+ }
+
+
+ void BasicMethodNodeImpl::setValue( const OUString&, const Any& )
+ {
+ throw UnknownPropertyException(
+ "BasicMethodNodeImpl::setValue: property name is unknown!" );
+ }
+
+
+ Any BasicMethodNodeImpl::getValue( const OUString& )
+ {
+ throw UnknownPropertyException(
+ "BasicMethodNodeImpl::getValue: property name is unknown!" );
+ }
+
+
+ sal_Bool BasicMethodNodeImpl::hasMethod( const OUString& aName )
+ {
+ bool bReturn = false;
+ if ( aName == BASPROV_PROPERTY_EDITABLE )
+ bReturn = true;
+
+ return bReturn;
+ }
+
+
+ sal_Bool BasicMethodNodeImpl::hasProperty( const OUString& )
+ {
+ return false;
+ }
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basmethnode.hxx b/scripting/source/basprov/basmethnode.hxx
new file mode 100644
index 0000000000..d1b08d837a
--- /dev/null
+++ b/scripting/source/basprov/basmethnode.hxx
@@ -0,0 +1,108 @@
+/* -*- 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 <bcholder.hxx>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/script/XInvocation.hpp>
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/uno3.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/implbase.hxx>
+
+
+class SbMethod;
+
+
+namespace basprov
+{
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::browse::XBrowseNode,
+ css::script::XInvocation > BasicMethodNodeImpl_BASE;
+
+ class BasicMethodNodeImpl : public BasicMethodNodeImpl_BASE,
+ public cppu::BaseMutex,
+ public ::scripting_helper::OBroadcastHelperHolder,
+ public ::comphelper::OPropertyContainer,
+ public ::comphelper::OPropertyArrayUsageHelper< BasicMethodNodeImpl >
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ OUString m_sScriptingContext;
+ SbMethod* m_pMethod;
+ bool m_bIsAppScript;
+
+ // properties
+ OUString m_sURI;
+ bool m_bEditable;
+
+ protected:
+ // OPropertySetHelper
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ public:
+ BasicMethodNodeImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ OUString sScriptingContext,
+ SbMethod* pMethod, bool isAppScript );
+ virtual ~BasicMethodNodeImpl() override;
+
+ // XInterface
+ DECLARE_XINTERFACE()
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XBrowseNode
+ virtual OUString SAL_CALL getName( ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override;
+ virtual sal_Bool SAL_CALL hasChildNodes( ) override;
+ virtual sal_Int16 SAL_CALL getType( ) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+ // XInvocation
+ virtual css::uno::Reference< css::beans::XIntrospectionAccess > SAL_CALL getIntrospection( ) override;
+ virtual css::uno::Any SAL_CALL invoke(
+ const OUString& aFunctionName,
+ const css::uno::Sequence< css::uno::Any >& aParams,
+ css::uno::Sequence< sal_Int16 >& aOutParamIndex,
+ css::uno::Sequence< css::uno::Any >& aOutParam ) override;
+ virtual void SAL_CALL setValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getValue( const OUString& aPropertyName ) override;
+ virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override;
+ };
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basmodnode.cxx b/scripting/source/basprov/basmodnode.cxx
new file mode 100644
index 0000000000..0ed8e91a9f
--- /dev/null
+++ b/scripting/source/basprov/basmodnode.cxx
@@ -0,0 +1,135 @@
+/* -*- 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 "basmodnode.hxx"
+#include "basmethnode.hxx"
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbmeth.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+
+
+namespace basprov
+{
+
+
+ // BasicModuleNodeImpl
+
+
+ BasicModuleNodeImpl::BasicModuleNodeImpl( const Reference< XComponentContext >& rxContext,
+ OUString sScriptingContext, SbModule* pModule, bool isAppScript )
+ :m_xContext( rxContext )
+ ,m_sScriptingContext(std::move( sScriptingContext ))
+ ,m_pModule( pModule )
+ ,m_bIsAppScript( isAppScript )
+ {
+ }
+
+
+ BasicModuleNodeImpl::~BasicModuleNodeImpl()
+ {
+ }
+
+
+ // XBrowseNode
+
+
+ OUString BasicModuleNodeImpl::getName( )
+ {
+ SolarMutexGuard aGuard;
+
+ OUString sModuleName;
+ if ( m_pModule )
+ sModuleName = m_pModule->GetName();
+
+ return sModuleName;
+ }
+
+
+ Sequence< Reference< browse::XBrowseNode > > BasicModuleNodeImpl::getChildNodes( )
+ {
+ SolarMutexGuard aGuard;
+
+ Sequence< Reference< browse::XBrowseNode > > aChildNodes;
+
+ if ( m_pModule )
+ {
+ SbxArray* pMethods = m_pModule->GetMethods().get();
+ if ( pMethods )
+ {
+ sal_uInt32 nCount = pMethods->Count();
+ sal_Int32 nRealCount = 0;
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Get(i));
+ if ( pMethod && !pMethod->IsHidden() )
+ ++nRealCount;
+ }
+ aChildNodes.realloc( nRealCount );
+ Reference< browse::XBrowseNode >* pChildNodes = aChildNodes.getArray();
+
+ sal_Int32 iTarget = 0;
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Get(i));
+ if ( pMethod && !pMethod->IsHidden() )
+ pChildNodes[iTarget++] = new BasicMethodNodeImpl(
+ m_xContext, m_sScriptingContext, pMethod, m_bIsAppScript);
+ }
+ }
+ }
+
+ return aChildNodes;
+ }
+
+
+ sal_Bool BasicModuleNodeImpl::hasChildNodes( )
+ {
+ SolarMutexGuard aGuard;
+
+ bool bReturn = false;
+ if ( m_pModule )
+ {
+ SbxArray* pMethods = m_pModule->GetMethods().get();
+ if (pMethods && pMethods->Count() > 0)
+ bReturn = true;
+ }
+
+ return bReturn;
+ }
+
+
+ sal_Int16 BasicModuleNodeImpl::getType( )
+ {
+ return browse::BrowseNodeTypes::CONTAINER;
+ }
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basmodnode.hxx b/scripting/source/basprov/basmodnode.hxx
new file mode 100644
index 0000000000..d34e709b28
--- /dev/null
+++ b/scripting/source/basprov/basmodnode.hxx
@@ -0,0 +1,64 @@
+/* -*- 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/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+
+class SbModule;
+
+
+namespace basprov
+{
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::browse::XBrowseNode > BasicModuleNodeImpl_BASE;
+
+
+ class BasicModuleNodeImpl : public BasicModuleNodeImpl_BASE
+ {
+ private:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ OUString m_sScriptingContext;
+ SbModule* m_pModule;
+ bool m_bIsAppScript;
+
+ public:
+ BasicModuleNodeImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ OUString sScriptingContext,
+ SbModule* pModule, bool isAppScript );
+ virtual ~BasicModuleNodeImpl() override;
+
+ // XBrowseNode
+ virtual OUString SAL_CALL getName( ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override;
+ virtual sal_Bool SAL_CALL hasChildNodes( ) override;
+ virtual sal_Int16 SAL_CALL getType( ) override;
+ };
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basprov.component b/scripting/source/basprov/basprov.component
new file mode 100644
index 0000000000..d0d6bf9749
--- /dev/null
+++ b/scripting/source/basprov/basprov.component
@@ -0,0 +1,29 @@
+<?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.scripting.ScriptProviderForBasic"
+ constructor="scripting_BasicProviderImpl_get_implementation">
+ <service name="com.sun.star.script.browse.BrowseNode"/>
+ <service name="com.sun.star.script.provider.LanguageScriptProvider"/>
+ <service name="com.sun.star.script.provider.ScriptProvider"/>
+ <service name="com.sun.star.script.provider.ScriptProviderForBasic"/>
+ </implementation>
+</component>
diff --git a/scripting/source/basprov/basprov.cxx b/scripting/source/basprov/basprov.cxx
new file mode 100644
index 0000000000..cafd4daa76
--- /dev/null
+++ b/scripting/source/basprov/basprov.cxx
@@ -0,0 +1,476 @@
+/* -*- 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 "basprov.hxx"
+#include "basscript.hxx"
+#include "baslibnode.hxx"
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+#include <vcl/svapp.hxx>
+#include <basic/basmgr.hxx>
+#include <basic/basicmanagerrepository.hxx>
+#include <basic/sbstar.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbmeth.hxx>
+#include <sfx2/app.hxx>
+
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <com/sun/star/script/XLibraryContainer2.hpp>
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/XUriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+
+#include <util/MiscUtils.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::document;
+using namespace ::sf_misc;
+
+
+namespace basprov
+{
+
+ // BasicProviderImpl
+
+
+ BasicProviderImpl::BasicProviderImpl( const Reference< XComponentContext >& xContext )
+ :m_pAppBasicManager( nullptr )
+ ,m_pDocBasicManager( nullptr )
+ ,m_xContext( xContext )
+ ,m_bIsAppScriptCtx( true )
+ ,m_bIsUserCtx(true)
+ {
+ }
+
+
+ BasicProviderImpl::~BasicProviderImpl()
+ {
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ }
+
+
+ bool BasicProviderImpl::isLibraryShared( const Reference< script::XLibraryContainer >& rxLibContainer, const OUString& rLibName )
+ {
+ bool bIsShared = false;
+
+ Reference< script::XLibraryContainer2 > xLibContainer( rxLibContainer, UNO_QUERY );
+ if ( xLibContainer.is() && xLibContainer->hasByName( rLibName ) && xLibContainer->isLibraryLink( rLibName ) )
+ {
+ OUString aFileURL;
+ if ( m_xContext.is() )
+ {
+ Reference< uri::XUriReferenceFactory > xUriFac( uri::UriReferenceFactory::create( m_xContext ) );
+
+ OUString aLinkURL( xLibContainer->getLibraryLinkURL( rLibName ) );
+ Reference< uri::XUriReference > xUriRef = xUriFac->parse( aLinkURL );
+
+ if ( xUriRef.is() )
+ {
+ OUString aScheme = xUriRef->getScheme();
+ if ( aScheme.equalsIgnoreAsciiCase("file") )
+ {
+ aFileURL = aLinkURL;
+ }
+ else if ( aScheme.equalsIgnoreAsciiCase("vnd.sun.star.pkg") )
+ {
+ OUString aDecodedURL = xUriRef->getAuthority();
+ if ( aDecodedURL.startsWithIgnoreAsciiCase( "vnd.sun.star.expand:", &aDecodedURL ) )
+ {
+ aDecodedURL = ::rtl::Uri::decode( aDecodedURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ Reference<util::XMacroExpander> xMacroExpander =
+ util::theMacroExpander::get(m_xContext);
+ aFileURL = xMacroExpander->expandMacros( aDecodedURL );
+ }
+ }
+ }
+ }
+
+ if ( !aFileURL.isEmpty() )
+ {
+ osl::DirectoryItem aFileItem;
+ osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL );
+ OSL_VERIFY( osl::DirectoryItem::get( aFileURL, aFileItem ) == osl::FileBase::E_None );
+ OSL_VERIFY( aFileItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None );
+ OUString aCanonicalFileURL( aFileStatus.getFileURL() );
+
+ if( aCanonicalFileURL.indexOf( "share/basic" ) != -1
+ || aCanonicalFileURL.indexOf( "share/uno_packages" ) != -1 )
+ bIsShared = true;
+ }
+ }
+
+ return bIsShared;
+ }
+
+ // SfxListener
+ void BasicProviderImpl::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+ {
+ if (auto pManager = dynamic_cast<const BasicManager*>(&rBC))
+ if (pManager == m_pAppBasicManager && rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListening(*m_pAppBasicManager);
+ m_pAppBasicManager = nullptr;
+ }
+ }
+
+ // XServiceInfo
+ OUString BasicProviderImpl::getImplementationName( )
+ {
+ return "com.sun.star.comp.scripting.ScriptProviderForBasic";
+ }
+
+ sal_Bool BasicProviderImpl::supportsService( const OUString& rServiceName )
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ Sequence< OUString > BasicProviderImpl::getSupportedServiceNames( )
+ {
+ return {
+ "com.sun.star.script.provider.ScriptProviderForBasic",
+ "com.sun.star.script.provider.LanguageScriptProvider",
+ "com.sun.star.script.provider.ScriptProvider",
+ "com.sun.star.script.browse.BrowseNode"};
+ }
+
+
+ // XInitialization
+
+
+ void BasicProviderImpl::initialize( const Sequence< Any >& aArguments )
+ {
+ // TODO
+
+ SolarMutexGuard aGuard;
+
+ if ( aArguments.getLength() != 1 )
+ {
+ throw IllegalArgumentException(
+ "BasicProviderImpl::initialize: incorrect argument count.",
+ *this,
+ 1
+ );
+ }
+
+ Reference< frame::XModel > xModel;
+
+ m_xInvocationContext.set( aArguments[0], UNO_QUERY );
+ if ( m_xInvocationContext.is() )
+ {
+ xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY );
+ if ( !xModel.is() )
+ {
+ throw IllegalArgumentException(
+ "BasicProviderImpl::initialize: unable to determine the document model from the script invocation context.",
+ *this,
+ 1
+ );
+ }
+ }
+ else
+ {
+ if ( !( aArguments[0] >>= m_sScriptingContext ) )
+ {
+ throw IllegalArgumentException(
+ "BasicProviderImpl::initialize: incorrect argument type " + aArguments[0].getValueTypeName(),
+ *this,
+ 1
+ );
+ }
+
+ if ( m_sScriptingContext.startsWith( "vnd.sun.star.tdoc" ) )
+ {
+ xModel = MiscUtils::tDocUrlToModel( m_sScriptingContext );
+ }
+ }
+
+ if ( xModel.is() )
+ {
+ Reference< XEmbeddedScripts > xDocumentScripts( xModel, UNO_QUERY );
+ if ( xDocumentScripts.is() )
+ {
+ m_pDocBasicManager = ::basic::BasicManagerRepository::getDocumentBasicManager( xModel );
+ m_xLibContainerDoc = xDocumentScripts->getBasicLibraries();
+ OSL_ENSURE( m_pDocBasicManager && m_xLibContainerDoc.is(),
+ "BasicProviderImpl::initialize: invalid BasicManager, or invalid script container!" );
+ }
+ m_bIsAppScriptCtx = false;
+ }
+ else
+ {
+ // Provider has been created with application context for user
+ // or share
+ if ( m_sScriptingContext != "user" )
+ {
+ m_bIsUserCtx = false;
+ }
+ else
+ {
+ /*
+ throw RuntimeException(
+ "BasicProviderImpl::initialize: no scripting context!" );
+ */
+ }
+ }
+
+ // TODO
+ if ( !m_pAppBasicManager )
+ {
+ m_pAppBasicManager = SfxApplication::GetBasicManager();
+ if (m_pAppBasicManager)
+ StartListening(*m_pAppBasicManager);
+ }
+
+ if ( !m_xLibContainerApp.is() )
+ m_xLibContainerApp = SfxGetpApp()->GetBasicContainer();
+ }
+
+
+ // XScriptProvider
+
+
+ Reference < provider::XScript > BasicProviderImpl::getScript( const OUString& scriptURI )
+ {
+ // TODO
+
+ SolarMutexGuard aGuard;
+
+ Reference< provider::XScript > xScript;
+ Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) );
+
+ Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI );
+
+ Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY );
+
+ if ( !uriRef.is() || !sfUri.is() )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "BasicProviderImpl::getScript: failed to parse URI: " + scriptURI,
+ Reference< XInterface >(),
+ scriptURI, "Basic",
+ provider::ScriptFrameworkErrorType::MALFORMED_URL );
+ }
+
+
+ OUString aDescription = sfUri->getName();
+ OUString aLocation = sfUri->getParameter( "location" );
+
+ sal_Int32 nIndex = 0;
+ // In some strange circumstances the Library name can have an
+ // apparently illegal '.' in it ( in imported VBA )
+
+ BasicManager* pBasicMgr = nullptr;
+ if ( aLocation == "document" )
+ {
+ pBasicMgr = m_pDocBasicManager;
+ }
+ else if ( aLocation == "application" )
+ {
+ pBasicMgr = m_pAppBasicManager;
+ }
+ OUString sProjectName;
+ if ( pBasicMgr )
+ sProjectName = pBasicMgr->GetName();
+
+ OUString aLibrary;
+ if ( !sProjectName.isEmpty() && aDescription.match( sProjectName ) )
+ {
+ SAL_WARN("scripting", "LibraryName " << sProjectName << " is part of the url " << aDescription );
+ aLibrary = sProjectName;
+ nIndex = sProjectName.getLength() + 1;
+ }
+ else
+ aLibrary = aDescription.getToken( 0, '.', nIndex );
+ OUString aModule;
+ if ( nIndex != -1 )
+ aModule = aDescription.getToken( 0, '.', nIndex );
+ OUString aMethod;
+ if ( nIndex != -1 )
+ aMethod = aDescription.getToken( 0, '.', nIndex );
+
+ if ( !aLibrary.isEmpty() && !aModule.isEmpty() && !aMethod.isEmpty() && !aLocation.isEmpty() )
+ {
+
+ if ( pBasicMgr )
+ {
+ StarBASIC* pBasic = pBasicMgr->GetLib( aLibrary );
+ if ( !pBasic )
+ {
+ sal_uInt16 nId = pBasicMgr->GetLibId( aLibrary );
+ if ( nId != LIB_NOTFOUND )
+ {
+ pBasicMgr->LoadLib( nId );
+ pBasic = pBasicMgr->GetLib( aLibrary );
+ }
+ }
+ if ( pBasic )
+ {
+ SbModule* pModule = pBasic->FindModule( aModule );
+ if ( pModule )
+ {
+ SbMethod* pMethod = pModule->FindMethod( aMethod, SbxClassType::Method );
+ if ( pMethod && !pMethod->IsHidden() )
+ {
+ if ( m_pDocBasicManager == pBasicMgr )
+ xScript = new BasicScriptImpl( aDescription, pMethod, *m_pDocBasicManager, m_xInvocationContext );
+ else
+ xScript = new BasicScriptImpl( aDescription, pMethod );
+ }
+ }
+ }
+ }
+ }
+
+ if ( !xScript.is() )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "The following Basic script could not be found:\n"
+ "library: '" + aLibrary + "'\n"
+ "module: '" + aModule + "'\n"
+ "method: '" + aMethod + "'\n"
+ "location: '" + aLocation + "'\n",
+ Reference< XInterface >(),
+ scriptURI, "Basic",
+ provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT );
+ }
+
+ return xScript;
+ }
+
+
+ // XBrowseNode
+
+
+ OUString BasicProviderImpl::getName( )
+ {
+ return "Basic";
+ }
+
+
+ Sequence< Reference< browse::XBrowseNode > > BasicProviderImpl::getChildNodes( )
+ {
+ SolarMutexGuard aGuard;
+
+ Reference< script::XLibraryContainer > xLibContainer;
+ BasicManager* pBasicManager = nullptr;
+
+ if ( m_bIsAppScriptCtx )
+ {
+ xLibContainer = m_xLibContainerApp;
+ pBasicManager = m_pAppBasicManager;
+ }
+ else
+ {
+ xLibContainer = m_xLibContainerDoc;
+ pBasicManager = m_pDocBasicManager;
+ }
+
+ Sequence< Reference< browse::XBrowseNode > > aChildNodes;
+
+ if ( pBasicManager && xLibContainer.is() )
+ {
+ const Sequence< OUString > aLibNames = xLibContainer->getElementNames();
+ sal_Int32 nLibCount = aLibNames.getLength();
+ aChildNodes.realloc( nLibCount );
+ Reference< browse::XBrowseNode >* pChildNodes = aChildNodes.getArray();
+ sal_Int32 childrenFound = 0;
+
+ for ( const OUString& rLibName : aLibNames )
+ {
+ bool bCreate = false;
+ if ( m_bIsAppScriptCtx )
+ {
+ const bool bShared = isLibraryShared( xLibContainer, rLibName );
+ if (m_bIsUserCtx != bShared)
+ bCreate = true;
+ }
+ else
+ {
+ bCreate = true;
+ }
+ if ( bCreate )
+ {
+ pChildNodes[childrenFound++]
+ = new BasicLibraryNodeImpl(m_xContext, m_sScriptingContext, pBasicManager,
+ xLibContainer, rLibName, m_bIsAppScriptCtx);
+ }
+ }
+
+ if ( childrenFound != nLibCount )
+ aChildNodes.realloc( childrenFound );
+ }
+
+ return aChildNodes;
+ }
+
+
+ sal_Bool BasicProviderImpl::hasChildNodes( )
+ {
+ SolarMutexGuard aGuard;
+
+ bool bReturn = false;
+ Reference< script::XLibraryContainer > xLibContainer;
+ if ( m_bIsAppScriptCtx )
+ {
+ xLibContainer = m_xLibContainerApp;
+ }
+ else
+ {
+ xLibContainer = m_xLibContainerDoc;
+ }
+ if ( xLibContainer.is() )
+ bReturn = xLibContainer->hasElements();
+
+ return bReturn;
+ }
+
+
+ sal_Int16 BasicProviderImpl::getType( )
+ {
+ return browse::BrowseNodeTypes::CONTAINER;
+ }
+
+
+ // component operations
+
+ extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ scripting_BasicProviderImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+ {
+ return cppu::acquire(new BasicProviderImpl(context));
+ }
+
+
+} // namespace basprov
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basprov.hxx b/scripting/source/basprov/basprov.hxx
new file mode 100644
index 0000000000..994113b628
--- /dev/null
+++ b/scripting/source/basprov/basprov.hxx
@@ -0,0 +1,97 @@
+/* -*- 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/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <svl/lstner.hxx>
+
+class BasicManager;
+
+
+namespace basprov
+{
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::script::provider::XScriptProvider,
+ css::script::browse::XBrowseNode > BasicProviderImpl_BASE;
+
+
+ class BasicProviderImpl : public BasicProviderImpl_BASE, public SfxListener
+ {
+ private:
+ BasicManager* m_pAppBasicManager;
+ BasicManager* m_pDocBasicManager;
+ css::uno::Reference< css::script::XLibraryContainer > m_xLibContainerApp;
+ css::uno::Reference< css::script::XLibraryContainer > m_xLibContainerDoc;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::document::XScriptInvocationContext > m_xInvocationContext;
+ OUString m_sScriptingContext;
+ bool m_bIsAppScriptCtx;
+ bool m_bIsUserCtx;
+
+ bool isLibraryShared(
+ const css::uno::Reference< css::script::XLibraryContainer >& rxLibContainer,
+ const OUString& rLibName );
+
+ public:
+ explicit BasicProviderImpl(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+ virtual ~BasicProviderImpl() 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XScriptProvider
+ virtual css::uno::Reference < css::script::provider::XScript > SAL_CALL getScript(
+ const OUString& scriptURI ) override;
+
+ // XBrowseNode
+ virtual OUString SAL_CALL getName( ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes( ) override;
+ virtual sal_Bool SAL_CALL hasChildNodes( ) override;
+ virtual sal_Int16 SAL_CALL getType( ) override;
+
+ protected:
+ // SfxListener
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+ };
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basscript.cxx b/scripting/source/basprov/basscript.cxx
new file mode 100644
index 0000000000..de50f62e11
--- /dev/null
+++ b/scripting/source/basprov/basscript.cxx
@@ -0,0 +1,315 @@
+/* -*- 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 "basscript.hxx"
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <basic/sbx.hxx>
+#include <basic/sbmod.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbuno.hxx>
+#include <basic/basmgr.hxx>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
+#include <bcholder.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <map>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::beans;
+
+
+namespace basprov
+{
+
+#define BASSCRIPT_PROPERTY_ID_CALLER 1
+constexpr OUString BASSCRIPT_PROPERTY_CALLER = u"Caller"_ustr;
+
+#define BASSCRIPT_DEFAULT_ATTRIBS() PropertyAttribute::BOUND | PropertyAttribute::TRANSIENT
+
+ typedef ::std::map< sal_Int16, Any > OutParamMap;
+
+
+ // BasicScriptImpl
+
+
+ BasicScriptImpl::BasicScriptImpl( OUString funcName, SbMethodRef xMethod )
+ : ::scripting_helper::OBroadcastHelperHolder( m_aMutex )
+ ,OPropertyContainer( GetBroadcastHelper() )
+ ,m_xMethod(std::move( xMethod ))
+ ,m_funcName(std::move( funcName ))
+ ,m_documentBasicManager( nullptr )
+ ,m_xDocumentScriptContext()
+ {
+ registerProperty( BASSCRIPT_PROPERTY_CALLER, BASSCRIPT_PROPERTY_ID_CALLER, BASSCRIPT_DEFAULT_ATTRIBS(), &m_caller, cppu::UnoType<decltype(m_caller)>::get() );
+ }
+
+
+ BasicScriptImpl::BasicScriptImpl( OUString funcName, SbMethodRef xMethod,
+ BasicManager& documentBasicManager, const Reference< XScriptInvocationContext >& documentScriptContext ) : ::scripting_helper::OBroadcastHelperHolder( m_aMutex )
+ ,OPropertyContainer( GetBroadcastHelper() )
+ ,m_xMethod(std::move( xMethod ))
+ ,m_funcName(std::move( funcName ))
+ ,m_documentBasicManager( &documentBasicManager )
+ ,m_xDocumentScriptContext( documentScriptContext )
+ {
+ StartListening( *m_documentBasicManager );
+ registerProperty( BASSCRIPT_PROPERTY_CALLER, BASSCRIPT_PROPERTY_ID_CALLER, BASSCRIPT_DEFAULT_ATTRIBS(), &m_caller, cppu::UnoType<decltype(m_caller)>::get() );
+ }
+
+
+ BasicScriptImpl::~BasicScriptImpl()
+ {
+ SolarMutexGuard g;
+
+ if ( m_documentBasicManager )
+ EndListening( *m_documentBasicManager );
+ }
+
+
+ // SfxListener
+
+ void BasicScriptImpl::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+ {
+ if ( &rBC != m_documentBasicManager )
+ {
+ OSL_ENSURE( false, "BasicScriptImpl::Notify: where does this come from?" );
+ // not interested in
+ return;
+ }
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ m_documentBasicManager = nullptr;
+ EndListening( rBC ); // prevent multiple notifications
+ }
+ }
+
+
+ // XInterface
+
+
+ IMPLEMENT_FORWARD_XINTERFACE2( BasicScriptImpl, BasicScriptImpl_BASE, OPropertyContainer )
+
+
+ // XTypeProvider
+
+
+ IMPLEMENT_FORWARD_XTYPEPROVIDER2( BasicScriptImpl, BasicScriptImpl_BASE, OPropertyContainer )
+
+
+ // OPropertySetHelper
+
+
+ ::cppu::IPropertyArrayHelper& BasicScriptImpl::getInfoHelper( )
+ {
+ return *getArrayHelper();
+ }
+
+
+ // OPropertyArrayUsageHelper
+
+
+ ::cppu::IPropertyArrayHelper* BasicScriptImpl::createArrayHelper( ) const
+ {
+ Sequence< Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+ }
+
+
+ // XPropertySet
+
+
+ Reference< XPropertySetInfo > BasicScriptImpl::getPropertySetInfo( )
+ {
+ Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+ }
+
+
+ // XScript
+
+
+ Any BasicScriptImpl::invoke( const Sequence< Any >& aParams, Sequence< sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam )
+ {
+ // TODO: throw CannotConvertException
+ // TODO: check length of aOutParamIndex, aOutParam
+
+ SolarMutexGuard aGuard;
+
+ Any aReturn;
+
+ if ( m_xMethod.is() )
+ {
+ // check if compiled
+ SbModule* pModule = static_cast< SbModule* >( m_xMethod->GetParent() );
+ if ( pModule && !pModule->IsCompiled() )
+ pModule->Compile();
+
+ // check number of parameters
+ sal_Int32 nParamsCount = aParams.getLength();
+ SbxInfo* pInfo = m_xMethod->GetInfo();
+ if ( pInfo )
+ {
+ sal_Int32 nSbxOptional = 0;
+ sal_uInt16 n = 1;
+ for ( const SbxParamInfo* pParamInfo = pInfo->GetParam( n ); pParamInfo; pParamInfo = pInfo->GetParam( ++n ) )
+ {
+ if ( pParamInfo->nFlags & SbxFlagBits::Optional )
+ ++nSbxOptional;
+ else
+ nSbxOptional = 0;
+ }
+ sal_Int32 nSbxCount = n - 1;
+ if ( nParamsCount < nSbxCount - nSbxOptional )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "wrong number of parameters!",
+ Reference< XInterface >(),
+ m_funcName,
+ "Basic",
+ provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT );
+ }
+ }
+
+ // set parameters
+ SbxArrayRef xSbxParams;
+ if ( nParamsCount > 0 )
+ {
+ xSbxParams = new SbxArray;
+ const Any* pParams = aParams.getConstArray();
+ for ( sal_Int32 i = 0; i < nParamsCount; ++i )
+ {
+ SbxVariableRef xSbxVar = new SbxVariable( SbxVARIANT );
+ unoToSbxValue( xSbxVar.get(), pParams[i] );
+ xSbxParams->Put(xSbxVar.get(), static_cast<sal_uInt32>(i) + 1);
+
+ if (pInfo)
+ {
+ if (auto* p = pInfo->GetParam(static_cast<sal_uInt16>(i) + 1))
+ {
+ SbxDataType t = static_cast<SbxDataType>(p->eType & 0x0FFF);
+ // tdf#133889 Revert the downcasting performed in sbxToUnoValueImpl
+ // to allow passing by reference.
+ SbxDataType a = xSbxVar->GetType();
+ if (t == SbxSINGLE && (a == SbxINTEGER || a == SbxLONG))
+ {
+ sal_Int32 val = xSbxVar->GetLong();
+ if (val >= -16777216 && val <= 16777215)
+ xSbxVar->SetType(t);
+ }
+ else if (t == SbxDOUBLE && (a == SbxINTEGER || a == SbxLONG))
+ xSbxVar->SetType(t);
+ else if (t == SbxLONG && a == SbxINTEGER)
+ xSbxVar->SetType(t);
+ else if (t == SbxULONG && a == SbxUSHORT)
+ xSbxVar->SetType(t);
+ // Enable passing by ref
+ if (t != SbxVARIANT)
+ xSbxVar->SetFlag(SbxFlagBits::Fixed);
+ }
+ }
+ }
+ }
+ if ( xSbxParams.is() )
+ m_xMethod->SetParameters( xSbxParams.get() );
+
+ // call method
+ SbxVariableRef xReturn = new SbxVariable;
+ ErrCode nErr = ERRCODE_NONE;
+
+ // if it's a document-based script, temporarily reset ThisComponent to the script invocation context
+ Any aOldThisComponent;
+ if ( m_documentBasicManager && m_xDocumentScriptContext.is() )
+ m_documentBasicManager->SetGlobalUNOConstant( "ThisComponent", Any( m_xDocumentScriptContext ), &aOldThisComponent );
+
+ if ( m_caller.hasElements() && m_caller[ 0 ].hasValue() )
+ {
+ SbxVariableRef xCallerVar = new SbxVariable( SbxVARIANT );
+ unoToSbxValue( xCallerVar.get(), m_caller[ 0 ] );
+ nErr = m_xMethod->Call( xReturn.get(), xCallerVar.get() );
+ }
+ else
+ nErr = m_xMethod->Call( xReturn.get() );
+
+ if ( m_documentBasicManager && m_xDocumentScriptContext.is() )
+ m_documentBasicManager->SetGlobalUNOConstant( "ThisComponent", aOldThisComponent );
+
+ if ( nErr != ERRCODE_NONE )
+ {
+ // TODO: throw InvocationTargetException ?
+ }
+
+ // get output parameters
+ if ( xSbxParams.is() )
+ {
+ SbxInfo* pInfo_ = m_xMethod->GetInfo();
+ if ( pInfo_ )
+ {
+ OutParamMap aOutParamMap;
+ for (sal_uInt32 n = 1, nCount = xSbxParams->Count(); n < nCount; ++n)
+ {
+ assert(nCount <= std::numeric_limits<sal_uInt16>::max());
+ const SbxParamInfo* pParamInfo = pInfo_->GetParam( sal::static_int_cast<sal_uInt16>(n) );
+ if ( pParamInfo && ( pParamInfo->eType & SbxBYREF ) != 0 )
+ {
+ SbxVariable* pVar = xSbxParams->Get(n);
+ if ( pVar )
+ {
+ SbxVariableRef xVar = pVar;
+ aOutParamMap.emplace( n - 1, sbxToUnoValue( xVar.get() ) );
+ }
+ }
+ }
+ sal_Int32 nOutParamCount = aOutParamMap.size();
+ aOutParamIndex.realloc( nOutParamCount );
+ aOutParam.realloc( nOutParamCount );
+ sal_Int16* pOutParamIndex = aOutParamIndex.getArray();
+ Any* pOutParam = aOutParam.getArray();
+ for ( const auto& rEntry : aOutParamMap )
+ {
+ *pOutParamIndex = rEntry.first;
+ ++pOutParamIndex;
+ *pOutParam = rEntry.second;
+ ++pOutParam;
+ }
+ }
+ }
+
+ // get return value
+ aReturn = sbxToUnoValue( xReturn.get() );
+
+ // reset parameters
+ m_xMethod->SetParameters( nullptr );
+ }
+
+ return aReturn;
+ }
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/basprov/basscript.hxx b/scripting/source/basprov/basscript.hxx
new file mode 100644
index 0000000000..e7a94d706e
--- /dev/null
+++ b/scripting/source/basprov/basscript.hxx
@@ -0,0 +1,103 @@
+/* -*- 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 <bcholder.hxx>
+#include <com/sun/star/script/provider/XScript.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <basic/sbmeth.hxx>
+#include <svl/lstner.hxx>
+
+class BasicManager;
+
+
+namespace basprov
+{
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::provider::XScript > BasicScriptImpl_BASE;
+
+
+ class BasicScriptImpl : public BasicScriptImpl_BASE, public SfxListener,
+ public cppu::BaseMutex,
+ public ::scripting_helper::OBroadcastHelperHolder,
+ public ::comphelper::OPropertyContainer,
+ public ::comphelper::OPropertyArrayUsageHelper< BasicScriptImpl >
+ {
+ private:
+ SbMethodRef m_xMethod;
+ OUString m_funcName;
+ BasicManager* m_documentBasicManager;
+ css::uno::Reference< css::document::XScriptInvocationContext >
+ m_xDocumentScriptContext;
+ // hack, OPropertyContainer doesn't allow you to define a property of unknown
+ // type ( I guess because an Any can't contain an Any... I've always wondered why?
+ // as it's not unusual to do that in corba )
+ css::uno::Sequence< css::uno::Any > m_caller;
+ protected:
+ // OPropertySetHelper
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ public:
+ BasicScriptImpl(
+ OUString funcName,
+ SbMethodRef xMethod
+ );
+ BasicScriptImpl(
+ OUString funcName,
+ SbMethodRef xMethod,
+ BasicManager& documentBasicManager,
+ const css::uno::Reference< css::document::XScriptInvocationContext >& documentScriptContext
+ );
+ virtual ~BasicScriptImpl() override;
+
+ // XInterface
+ DECLARE_XINTERFACE()
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+
+ // XScript
+ virtual css::uno::Any SAL_CALL invoke(
+ const css::uno::Sequence< css::uno::Any >& aParams,
+ css::uno::Sequence< sal_Int16 >& aOutParamIndex,
+ css::uno::Sequence< css::uno::Any >& aOutParam ) override;
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+
+ // SfxListener
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ };
+
+
+} // namespace basprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/dlgprov/DialogModelProvider.cxx b/scripting/source/dlgprov/DialogModelProvider.cxx
new file mode 100644
index 0000000000..e49ed058d4
--- /dev/null
+++ b/scripting/source/dlgprov/DialogModelProvider.cxx
@@ -0,0 +1,166 @@
+/* -*- 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 "DialogModelProvider.hxx"
+#include "dlgprov.hxx"
+#include <com/sun/star/resource/XStringResourceManager.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+
+/// anonymous implementation namespace
+namespace dlgprov {
+
+using namespace ::com::sun::star;
+using namespace awt;
+using namespace lang;
+using namespace uno;
+using namespace script;
+using namespace beans;
+
+
+DialogModelProvider::DialogModelProvider(Reference< XComponentContext > const & context) :
+ m_xContext(context)
+{}
+
+// lang::XInitialization:
+void SAL_CALL DialogModelProvider::initialize(const css::uno::Sequence< uno::Any > & aArguments)
+{
+ if ( aArguments.getLength() != 1 )
+ return;
+
+ OUString sURL;
+ if ( !( aArguments[ 0 ] >>= sURL ))
+ throw css::lang::IllegalArgumentException();
+ // Try any other URL with SimpleFileAccess
+ Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create(m_xContext);
+
+ try
+ {
+ Reference< io::XInputStream > xInput = xSFI->openFileRead( sURL );
+ Reference< resource::XStringResourceManager > xStringResourceManager;
+ if ( xInput.is() )
+ {
+ xStringResourceManager = dlgprov::lcl_getStringResourceManager(m_xContext,sURL);
+ Any aDialogSourceURLAny;
+ aDialogSourceURLAny <<= sURL;
+
+ Reference< frame::XModel > xModel;
+ m_xDialogModel.set( dlgprov::lcl_createDialogModel( m_xContext, xInput , xModel, xStringResourceManager, aDialogSourceURLAny ), UNO_SET_THROW);
+ m_xDialogModelProp.set(m_xDialogModel, UNO_QUERY_THROW);
+ }
+ }
+ catch( Exception& )
+ {}
+ //m_sURL = sURL;
+}
+
+// container::XElementAccess:
+uno::Type SAL_CALL DialogModelProvider::getElementType()
+{
+ return m_xDialogModel->getElementType();
+}
+
+sal_Bool SAL_CALL DialogModelProvider::hasElements()
+{
+ return m_xDialogModel->hasElements();
+}
+
+// container::XNameAccess:
+uno::Any SAL_CALL DialogModelProvider::getByName(const OUString & aName)
+{
+ return m_xDialogModel->getByName(aName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DialogModelProvider::getElementNames()
+{
+ return m_xDialogModel->getElementNames();
+}
+
+sal_Bool SAL_CALL DialogModelProvider::hasByName(const OUString & aName)
+{
+ return m_xDialogModel->hasByName(aName);
+}
+
+// container::XNameReplace:
+void SAL_CALL DialogModelProvider::replaceByName(const OUString & aName, const uno::Any & aElement)
+{
+ m_xDialogModel->replaceByName(aName,aElement);
+}
+
+// container::XNameContainer:
+void SAL_CALL DialogModelProvider::insertByName(const OUString & aName, const uno::Any & aElement)
+{
+ m_xDialogModel->insertByName(aName,aElement);
+}
+
+void SAL_CALL DialogModelProvider::removeByName(const OUString & aName)
+{
+ m_xDialogModel->removeByName(aName);
+}
+uno::Reference< beans::XPropertySetInfo > SAL_CALL DialogModelProvider::getPropertySetInfo( )
+{
+ return m_xDialogModelProp->getPropertySetInfo();
+}
+void SAL_CALL DialogModelProvider::setPropertyValue( const OUString&, const uno::Any& )
+{
+}
+uno::Any SAL_CALL DialogModelProvider::getPropertyValue( const OUString& PropertyName )
+{
+ return m_xDialogModelProp->getPropertyValue(PropertyName);
+}
+void SAL_CALL DialogModelProvider::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& )
+{
+}
+void SAL_CALL DialogModelProvider::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& )
+{
+}
+void SAL_CALL DialogModelProvider::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& )
+{
+}
+void SAL_CALL DialogModelProvider::removeVetoableChangeListener( const OUString& ,const uno::Reference< beans::XVetoableChangeListener >& )
+{
+}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL DialogModelProvider::getImplementationName()
+{
+ return "com.sun.star.comp.scripting.DialogModelProvider";
+}
+
+sal_Bool SAL_CALL DialogModelProvider::supportsService(OUString const & serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DialogModelProvider::getSupportedServiceNames()
+{
+ return { "com.sun.star.awt.UnoControlDialogModelProvider" };
+}
+
+} // closing anonymous implementation namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_DialogModelProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new dlgprov::DialogModelProvider(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/dlgprov/DialogModelProvider.hxx b/scripting/source/dlgprov/DialogModelProvider.hxx
new file mode 100644
index 0000000000..d9a41ef64e
--- /dev/null
+++ b/scripting/source/dlgprov/DialogModelProvider.hxx
@@ -0,0 +1,87 @@
+/* -*- 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 <cppuhelper/factory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+/// anonymous implementation namespace
+namespace dlgprov{
+
+class DialogModelProvider:
+ public ::cppu::WeakImplHelper<
+ css::lang::XInitialization,
+ css::container::XNameContainer,
+ css::beans::XPropertySet,
+ css::lang::XServiceInfo>
+{
+public:
+ explicit DialogModelProvider(css::uno::Reference< css::uno::XComponentContext > const & context);
+private:
+ // css::lang::XInitialization:
+ virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > & aArguments) override;
+
+ // css::container::XElementAccess:
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // css::container::XNameAccess:
+ virtual css::uno::Any SAL_CALL getByName(const OUString & aName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName(const OUString & aName) override;
+
+ // css::container::XNameReplace:
+ virtual void SAL_CALL replaceByName(const OUString & aName, const css::uno::Any & aElement) override;
+
+ // css::container::XNameContainer:
+ virtual void SAL_CALL insertByName(const OUString & aName, const css::uno::Any & aElement) override;
+ virtual void SAL_CALL removeByName(const OUString & Name) override;
+
+ // css::lang::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;
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+private:
+ DialogModelProvider(const DialogModelProvider &) = delete;
+ DialogModelProvider& operator=(const DialogModelProvider &) = delete;
+
+ // destructor is private and will be called indirectly by the release call virtual ~DialogModelProvider() {}
+
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::container::XNameContainer> m_xDialogModel;
+ css::uno::Reference< css::beans::XPropertySet> m_xDialogModelProp;
+};
+} // closing anonymous implementation namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/dlgprov/dlgevtatt.cxx b/scripting/source/dlgprov/dlgevtatt.cxx
new file mode 100644
index 0000000000..d697628c4f
--- /dev/null
+++ b/scripting/source/dlgprov/dlgevtatt.cxx
@@ -0,0 +1,659 @@
+/* -*- 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 "dlgevtatt.hxx"
+
+#include "dlgprov.hxx"
+
+#include <sfx2/strings.hrc>
+#include <sfx2/sfxresid.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XDialogEventHandler.hpp>
+#include <com/sun/star/awt/XContainerWindowEventHandler.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XScriptEventsSupplier.hpp>
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/reflection/XIdlMethod.hpp>
+#include <com/sun/star/beans/MethodConcept.hpp>
+#include <com/sun/star/beans/XMaterialHolder.hpp>
+
+#include <ooo/vba/XVBAToOOEventDescGen.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::reflection;
+
+
+namespace dlgprov
+{
+ namespace {
+
+ class DialogSFScriptListenerImpl : public DialogScriptListenerImpl
+ {
+ protected:
+ Reference< frame::XModel > m_xModel;
+ virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override;
+ public:
+ DialogSFScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< frame::XModel >& rxModel ) : DialogScriptListenerImpl( rxContext ), m_xModel( rxModel ) {}
+ };
+
+ class DialogLegacyScriptListenerImpl : public DialogSFScriptListenerImpl
+ {
+ protected:
+ virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override;
+ public:
+ DialogLegacyScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< frame::XModel >& rxModel ) : DialogSFScriptListenerImpl( rxContext, rxModel ){}
+ };
+
+ class DialogUnoScriptListenerImpl : public DialogSFScriptListenerImpl
+ {
+ Reference< awt::XControl > m_xControl;
+ Reference< XInterface > m_xHandler;
+ Reference< beans::XIntrospectionAccess > m_xIntrospectionAccess;
+ bool m_bDialogProviderMode;
+
+ virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override;
+
+ public:
+ DialogUnoScriptListenerImpl( const Reference< XComponentContext >& rxContext,
+ const Reference< frame::XModel >& rxModel,
+ const Reference< awt::XControl >& rxControl,
+ const Reference< XInterface >& rxHandler,
+ const Reference< beans::XIntrospectionAccess >& rxIntrospectionAccess,
+ bool bDialogProviderMode ); // false: ContainerWindowProvider mode
+
+ };
+
+ class DialogVBAScriptListenerImpl : public DialogScriptListenerImpl
+ {
+ protected:
+ OUString msDialogCodeName;
+ OUString msDialogLibName;
+ Reference< script::XScriptListener > mxListener;
+ virtual void firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* pRet ) override;
+ public:
+ DialogVBAScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< awt::XControl >& rxControl, const Reference< frame::XModel >& xModel, OUString sDialogLibName );
+ };
+
+ }
+
+ DialogVBAScriptListenerImpl::DialogVBAScriptListenerImpl( const Reference< XComponentContext >& rxContext, const Reference< awt::XControl >& rxControl, const Reference< frame::XModel >& xModel, OUString sDialogLibName ) : DialogScriptListenerImpl( rxContext ), msDialogLibName(std::move( sDialogLibName ))
+ {
+ Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager() );
+ Sequence< Any > args(1);
+ if ( xSMgr.is() )
+ {
+ args.getArray()[0] <<= xModel;
+ mxListener.set( xSMgr->createInstanceWithArgumentsAndContext( "ooo.vba.EventListener", args, m_xContext ), UNO_QUERY );
+ }
+ if ( !rxControl.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySet > xProps( rxControl->getModel(), UNO_QUERY_THROW );
+ xProps->getPropertyValue("Name") >>= msDialogCodeName;
+ xProps.set( mxListener, UNO_QUERY_THROW );
+ xProps->setPropertyValue("Model", args[ 0 ] );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+
+ }
+
+ void DialogVBAScriptListenerImpl::firing_impl( const script::ScriptEvent& aScriptEvent, uno::Any* )
+ {
+ if ( !(aScriptEvent.ScriptType == "VBAInterop" && mxListener.is()) )
+ return;
+
+ ScriptEvent aScriptEventCopy( aScriptEvent );
+ aScriptEventCopy.ScriptCode = msDialogLibName + "." + msDialogCodeName;
+ try
+ {
+ mxListener->firing( aScriptEventCopy );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+ }
+
+
+ // DialogEventsAttacherImpl
+
+
+ DialogEventsAttacherImpl::DialogEventsAttacherImpl( const Reference< XComponentContext >& rxContext, const Reference< frame::XModel >& rxModel, const Reference< awt::XControl >& rxControl, const Reference< XInterface >& rxHandler, const Reference< beans::XIntrospectionAccess >& rxIntrospect, bool bProviderMode, const Reference< script::XScriptListener >& rxRTLListener, const OUString& sDialogLibName )
+ :mbUseFakeVBAEvents( false ), m_xContext( rxContext )
+ {
+ // key listeners by protocol when ScriptType = 'Script'
+ // otherwise key is the ScriptType e.g. StarBasic
+ if ( rxRTLListener.is() ) // set up handler for RTL_BASIC
+ listenersForTypes[ OUString("StarBasic") ] = rxRTLListener;
+ else
+ listenersForTypes[ OUString("StarBasic") ] = new DialogLegacyScriptListenerImpl( rxContext, rxModel );
+ // handler for Script & OUString("vnd.sun.star.UNO:")
+ listenersForTypes[ OUString("vnd.sun.star.UNO") ] = new DialogUnoScriptListenerImpl( rxContext, rxModel, rxControl, rxHandler, rxIntrospect, bProviderMode );
+ listenersForTypes[ OUString("vnd.sun.star.script") ] = new DialogSFScriptListenerImpl( rxContext, rxModel );
+
+ // determine the VBA compatibility mode from the Basic library container
+ try
+ {
+ uno::Reference< beans::XPropertySet > xModelProps( rxModel, uno::UNO_QUERY_THROW );
+ uno::Reference< script::vba::XVBACompatibility > xVBACompat(
+ xModelProps->getPropertyValue("BasicLibraries"), uno::UNO_QUERY_THROW );
+ mbUseFakeVBAEvents = xVBACompat->getVBACompatibilityMode();
+ }
+ catch( uno::Exception& )
+ {
+ }
+ if ( mbUseFakeVBAEvents )
+ listenersForTypes[ OUString("VBAInterop") ] = new DialogVBAScriptListenerImpl( rxContext, rxControl, rxModel, sDialogLibName );
+ }
+
+
+ DialogEventsAttacherImpl::~DialogEventsAttacherImpl()
+ {
+ }
+
+
+ Reference< script::XScriptListener > const &
+ DialogEventsAttacherImpl::getScriptListenerForKey( const OUString& sKey )
+ {
+ ListenerHash::iterator it = listenersForTypes.find( sKey );
+ if ( it == listenersForTypes.end() )
+ throw RuntimeException(); // more text info here please
+ return it->second;
+ }
+ Reference< XScriptEventsSupplier > DialogEventsAttacherImpl::getFakeVbaEventsSupplier( const Reference< XControl >& xControl, OUString const & sControlName )
+ {
+ Reference< XScriptEventsSupplier > xEventsSupplier;
+ Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager() );
+ if ( xSMgr.is() )
+ {
+ Reference< ooo::vba::XVBAToOOEventDescGen > xVBAToOOEvtDesc( xSMgr->createInstanceWithContext("ooo.vba.VBAToOOEventDesc", m_xContext ), UNO_QUERY );
+ if ( xVBAToOOEvtDesc.is() )
+ xEventsSupplier = xVBAToOOEvtDesc->getEventSupplier( xControl, sControlName );
+
+ }
+ return xEventsSupplier;
+ }
+
+
+ void DialogEventsAttacherImpl::attachEventsToControl( const Reference< XControl>& xControl, const Reference< XScriptEventsSupplier >& xEventsSupplier, const Any& Helper )
+ {
+ if ( !xEventsSupplier.is() )
+ return;
+
+ Reference< container::XNameContainer > xEventCont = xEventsSupplier->getEvents();
+
+ Reference< XControlModel > xControlModel = xControl->getModel();
+ if ( !xEventCont.is() )
+ return;
+
+ const Sequence< OUString > aNames = xEventCont->getElementNames();
+
+ for ( const OUString& rName : aNames )
+ {
+ ScriptEventDescriptor aDesc;
+
+ Any aElement = xEventCont->getByName( rName );
+ aElement >>= aDesc;
+ OUString sKey = aDesc.ScriptType;
+ if ( aDesc.ScriptType == "Script" || aDesc.ScriptType == "UNO" )
+ {
+ sal_Int32 nIndex = aDesc.ScriptCode.indexOf( ':' );
+ sKey = aDesc.ScriptCode.copy( 0, nIndex );
+ }
+ Reference< XAllListener > xAllListener =
+ new DialogAllListenerImpl( getScriptListenerForKey( sKey ), aDesc.ScriptType, aDesc.ScriptCode );
+
+ // try first to attach event to the ControlModel
+ bool bSuccess = false;
+ try
+ {
+ Reference< XEventListener > xListener_ = m_xEventAttacher->attachSingleEventListener(
+ xControlModel, xAllListener, Helper, aDesc.ListenerType,
+ aDesc.AddListenerParam, aDesc.EventMethod );
+
+ if ( xListener_.is() )
+ bSuccess = true;
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+
+ try
+ {
+ // if we had no success, try to attach to the control
+ if ( !bSuccess )
+ {
+ m_xEventAttacher->attachSingleEventListener(
+ xControl, xAllListener, Helper, aDesc.ListenerType,
+ aDesc.AddListenerParam, aDesc.EventMethod );
+ }
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+ }
+ }
+
+
+ void DialogEventsAttacherImpl::nestedAttachEvents( const Sequence< Reference< XInterface > >& Objects, const Any& Helper, OUString& sDialogCodeName )
+ {
+ for ( const Reference< XInterface >& rObject : Objects )
+ {
+ // We know that we have to do with instances of XControl.
+ // Otherwise this is not the right implementation for
+ // XScriptEventsAttacher and we have to give up.
+ Reference< XControl > xControl( rObject, UNO_QUERY );
+ Reference< XControlContainer > xControlContainer( xControl, UNO_QUERY );
+ Reference< XDialog > xDialog( xControl, UNO_QUERY );
+ if ( !xControl.is() )
+ throw IllegalArgumentException();
+
+ // get XEventsSupplier from control model
+ Reference< XControlModel > xControlModel = xControl->getModel();
+ Reference< XScriptEventsSupplier > xEventsSupplier( xControlModel, UNO_QUERY );
+ attachEventsToControl( xControl, xEventsSupplier, Helper );
+ if ( mbUseFakeVBAEvents )
+ {
+ xEventsSupplier.set( getFakeVbaEventsSupplier( xControl, sDialogCodeName ) );
+ Any newHelper(xControl );
+ attachEventsToControl( xControl, xEventsSupplier, newHelper );
+ }
+ if ( xControlContainer.is() && !xDialog.is() )
+ {
+ Sequence< Reference< XControl > > aControls = xControlContainer->getControls();
+ sal_Int32 nControlCount = aControls.getLength();
+
+ Sequence< Reference< XInterface > > aObjects( nControlCount );
+ Reference< XInterface >* pObjects2 = aObjects.getArray();
+ const Reference< XControl >* pControls = aControls.getConstArray();
+
+ for ( sal_Int32 i2 = 0; i2 < nControlCount; ++i2 )
+ {
+ pObjects2[i2].set( pControls[i2], UNO_QUERY );
+ }
+ nestedAttachEvents( aObjects, Helper, sDialogCodeName );
+ }
+ }
+ }
+
+
+ // XScriptEventsAttacher
+
+
+ void SAL_CALL DialogEventsAttacherImpl::attachEvents( const Sequence< Reference< XInterface > >& Objects,
+ const css::uno::Reference<css::script::XScriptListener>&,
+ const Any& Helper )
+ {
+ // get EventAttacher
+ {
+ ::osl::MutexGuard aGuard( getMutex() );
+
+ if ( !m_xEventAttacher.is() )
+ {
+ Reference< XMultiComponentFactory > xSMgr( m_xContext->getServiceManager() );
+ if ( !xSMgr.is() )
+ throw RuntimeException();
+
+ m_xEventAttacher.set( xSMgr->createInstanceWithContext(
+ "com.sun.star.script.EventAttacher", m_xContext ), UNO_QUERY );
+
+ if ( !m_xEventAttacher.is() )
+ throw ServiceNotRegisteredException();
+ }
+ }
+ OUString sDialogCodeName;
+ sal_Int32 nObjCount = Objects.getLength();
+ Reference< awt::XControl > xDlgControl( Objects[ nObjCount - 1 ], uno::UNO_QUERY ); // last object is the dialog
+ if ( xDlgControl.is() )
+ {
+ Reference< XPropertySet > xProps( xDlgControl->getModel(), UNO_QUERY );
+ try
+ {
+ xProps->getPropertyValue("Name") >>= sDialogCodeName;
+ }
+ catch( Exception& ){}
+ }
+ // go over all objects
+ nestedAttachEvents( Objects, Helper, sDialogCodeName );
+ }
+
+
+ // DialogAllListenerImpl
+
+
+ DialogAllListenerImpl::DialogAllListenerImpl( const Reference< XScriptListener >& rxListener,
+ OUString sScriptType, OUString sScriptCode )
+ :m_xScriptListener( rxListener )
+ ,m_sScriptType(std::move( sScriptType ))
+ ,m_sScriptCode(std::move( sScriptCode ))
+ {
+ }
+
+
+ DialogAllListenerImpl::~DialogAllListenerImpl()
+ {
+ }
+
+
+ void DialogAllListenerImpl::firing_impl( const AllEventObject& Event, Any* pRet )
+ {
+ ScriptEvent aScriptEvent;
+ aScriptEvent.Source = getXWeak(); // get correct XInterface
+ aScriptEvent.ListenerType = Event.ListenerType;
+ aScriptEvent.MethodName = Event.MethodName;
+ aScriptEvent.Arguments = Event.Arguments;
+ aScriptEvent.Helper = Event.Helper;
+ aScriptEvent.ScriptType = m_sScriptType;
+ aScriptEvent.ScriptCode = m_sScriptCode;
+
+ if ( m_xScriptListener.is() )
+ {
+ if ( pRet )
+ *pRet = m_xScriptListener->approveFiring( aScriptEvent );
+ else
+ m_xScriptListener->firing( aScriptEvent );
+ }
+ }
+
+
+ // XEventListener
+
+
+ void DialogAllListenerImpl::disposing(const EventObject& )
+ {
+ }
+
+
+ // XAllListener
+
+
+ void DialogAllListenerImpl::firing( const AllEventObject& Event )
+ {
+ //::osl::MutexGuard aGuard( getMutex() );
+
+ firing_impl( Event, nullptr );
+ }
+
+
+ Any DialogAllListenerImpl::approveFiring( const AllEventObject& Event )
+ {
+ //::osl::MutexGuard aGuard( getMutex() );
+
+ Any aReturn;
+ firing_impl( Event, &aReturn );
+ return aReturn;
+ }
+
+
+ // DialogScriptListenerImpl
+
+
+ DialogUnoScriptListenerImpl::DialogUnoScriptListenerImpl( const Reference< XComponentContext >& rxContext,
+ const Reference< css::frame::XModel >& rxModel,
+ const Reference< css::awt::XControl >& rxControl,
+ const Reference< css::uno::XInterface >& rxHandler,
+ const Reference< css::beans::XIntrospectionAccess >& rxIntrospectionAccess,
+ bool bDialogProviderMode )
+ : DialogSFScriptListenerImpl( rxContext, rxModel )
+ ,m_xControl( rxControl )
+ ,m_xHandler( rxHandler )
+ ,m_xIntrospectionAccess( rxIntrospectionAccess )
+ ,m_bDialogProviderMode( bDialogProviderMode )
+ {
+ }
+
+
+ DialogScriptListenerImpl::~DialogScriptListenerImpl()
+ {
+ }
+
+
+ void DialogSFScriptListenerImpl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet )
+ {
+ try
+ {
+ Reference< provider::XScriptProvider > xScriptProvider;
+ if ( m_xModel.is() )
+ {
+ Reference< provider::XScriptProviderSupplier > xSupplier( m_xModel, UNO_QUERY );
+ OSL_ENSURE( xSupplier.is(), "DialogScriptListenerImpl::firing_impl: failed to get script provider supplier" );
+ if ( xSupplier.is() )
+ xScriptProvider.set( xSupplier->getScriptProvider() );
+ }
+ else
+ {
+ OSL_ASSERT( m_xContext.is() );
+ if ( m_xContext.is() )
+ {
+ Reference< provider::XScriptProviderFactory > xFactory =
+ provider::theMasterScriptProviderFactory::get( m_xContext );
+
+ Any aCtx;
+ aCtx <<= OUString("user");
+ xScriptProvider = xFactory->createScriptProvider( aCtx );
+ }
+ }
+
+ OSL_ENSURE( xScriptProvider.is(), "DialogScriptListenerImpl::firing_impl: failed to get script provider" );
+
+ if ( xScriptProvider.is() )
+ {
+ Reference< provider::XScript > xScript = xScriptProvider->getScript( aScriptEvent.ScriptCode );
+ OSL_ENSURE( xScript.is(), "DialogScriptListenerImpl::firing_impl: failed to get script" );
+
+ if ( xScript.is() )
+ {
+ Sequence< Any > aInParams;
+ Sequence< sal_Int16 > aOutParamsIndex;
+ Sequence< Any > aOutParams;
+
+ // get arguments for script
+ aInParams = aScriptEvent.Arguments;
+
+ Any aResult = xScript->invoke( aInParams, aOutParamsIndex, aOutParams );
+ if ( pRet )
+ *pRet = aResult;
+ }
+ }
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+ }
+
+ void DialogLegacyScriptListenerImpl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet )
+ {
+ OUString sScriptURL;
+ OUString sScriptCode( aScriptEvent.ScriptCode );
+
+ if ( aScriptEvent.ScriptType != "StarBasic" )
+ return;
+
+ // StarBasic script: convert ScriptCode to scriptURL
+ sal_Int32 nIndex = sScriptCode.indexOf( ':' );
+ if ( nIndex >= 0 && nIndex < sScriptCode.getLength() )
+ {
+ sScriptURL = OUString::Concat("vnd.sun.star.script:") +
+ sScriptCode.subView( nIndex + 1 ) +
+ "?language=Basic&location=" +
+ sScriptCode.subView( 0, nIndex );
+ }
+ ScriptEvent aSFScriptEvent( aScriptEvent );
+ aSFScriptEvent.ScriptCode = sScriptURL;
+ DialogSFScriptListenerImpl::firing_impl( aSFScriptEvent, pRet );
+ }
+
+ void DialogUnoScriptListenerImpl::firing_impl( const ScriptEvent& aScriptEvent, Any* pRet )
+ {
+ OUString aMethodName = aScriptEvent.ScriptCode.copy( strlen("vnd.sun.star.UNO:") );
+
+ const Any* pArguments = aScriptEvent.Arguments.getConstArray();
+ Any aEventObject = pArguments[0];
+
+ bool bHandled = false;
+ if( m_xHandler.is() )
+ {
+ if( m_bDialogProviderMode )
+ {
+ Reference< XDialogEventHandler > xDialogEventHandler( m_xHandler, UNO_QUERY );
+ if( xDialogEventHandler.is() )
+ {
+ Reference< XDialog > xDialog( m_xControl, UNO_QUERY );
+ bHandled = xDialogEventHandler->callHandlerMethod( xDialog, aEventObject, aMethodName );
+ }
+ }
+ else
+ {
+ Reference< XContainerWindowEventHandler > xContainerWindowEventHandler( m_xHandler, UNO_QUERY );
+ if( xContainerWindowEventHandler.is() )
+ {
+ Reference< XWindow > xWindow( m_xControl, UNO_QUERY );
+ bHandled = xContainerWindowEventHandler->callHandlerMethod( xWindow, aEventObject, aMethodName );
+ }
+ }
+ }
+
+ Any aRet;
+ if( !bHandled && m_xIntrospectionAccess.is() )
+ {
+ try
+ {
+ // call method
+ const Reference< XIdlMethod >& rxMethod = m_xIntrospectionAccess->
+ getMethod( aMethodName, MethodConcept::ALL - MethodConcept::DANGEROUS );
+
+ Reference< XMaterialHolder > xMaterialHolder =
+ Reference< XMaterialHolder >::query( m_xIntrospectionAccess );
+ Any aHandlerObject = xMaterialHolder->getMaterial();
+
+ Sequence< Reference< XIdlClass > > aParamTypeSeq = rxMethod->getParameterTypes();
+ sal_Int32 nParamCount = aParamTypeSeq.getLength();
+ if( nParamCount == 0 )
+ {
+ Sequence<Any> args;
+ rxMethod->invoke( aHandlerObject, args );
+ bHandled = true;
+ }
+ else if( nParamCount == 2 )
+ {
+ // Signature check automatically done by reflection
+ Sequence<Any> Args(2);
+ Any* pArgs = Args.getArray();
+ if( m_bDialogProviderMode )
+ {
+ Reference< XDialog > xDialog( m_xControl, UNO_QUERY );
+ pArgs[0] <<= xDialog;
+ }
+ else
+ {
+ Reference< XWindow > xWindow( m_xControl, UNO_QUERY );
+ pArgs[0] <<= xWindow;
+ }
+ pArgs[1] = aEventObject;
+ aRet = rxMethod->invoke( aHandlerObject, Args );
+ bHandled = true;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+ }
+
+ if( bHandled )
+ {
+ if( pRet )
+ *pRet = aRet;
+ }
+ else
+ {
+ OUString aRes(SfxResId(STR_ERRUNOEVENTBINDUNG));
+ OUString aQuoteChar( "\"" );
+
+ sal_Int32 nIndex = aRes.indexOf( '%' );
+
+ OUString aOUFinal =
+ aRes.subView( 0, nIndex ) +
+ aQuoteChar + aMethodName + aQuoteChar +
+ aRes.subView( nIndex + 2 );
+
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok, aOUFinal));
+ xBox->run();
+ }
+ }
+
+
+ // XEventListener
+
+
+ void DialogScriptListenerImpl::disposing(const EventObject& )
+ {
+ }
+
+
+ // XScriptListener
+
+
+ void DialogScriptListenerImpl::firing( const ScriptEvent& aScriptEvent )
+ {
+ //::osl::MutexGuard aGuard( getMutex() );
+
+ firing_impl( aScriptEvent, nullptr );
+ }
+
+
+ Any DialogScriptListenerImpl::approveFiring( const ScriptEvent& aScriptEvent )
+ {
+ //::osl::MutexGuard aGuard( getMutex() );
+
+ Any aReturn;
+ firing_impl( aScriptEvent, &aReturn );
+ return aReturn;
+ }
+
+
+} // namespace dlgprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/dlgprov/dlgevtatt.hxx b/scripting/source/dlgprov/dlgevtatt.hxx
new file mode 100644
index 0000000000..62a131de03
--- /dev/null
+++ b/scripting/source/dlgprov/dlgevtatt.hxx
@@ -0,0 +1,131 @@
+/* -*- 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/frame/XModel.hpp>
+#include <com/sun/star/script/XAllListener.hpp>
+#include <com/sun/star/script/XEventAttacher.hpp>
+#include <com/sun/star/script/XScriptEventsAttacher.hpp>
+#include <com/sun/star/script/XScriptListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/script/XScriptEventsSupplier.hpp>
+
+#include <unordered_map>
+#include <utility>
+
+namespace dlgprov
+{
+ typedef std::unordered_map< OUString,
+ css::uno::Reference< css::script::XScriptListener > > ListenerHash;
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::XScriptEventsAttacher > DialogEventsAttacherImpl_BASE;
+
+
+ class DialogEventsAttacherImpl : public DialogEventsAttacherImpl_BASE
+ {
+ private:
+ bool mbUseFakeVBAEvents;
+ ListenerHash listenersForTypes;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::script::XEventAttacher > m_xEventAttacher;
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::script::XScriptListener > const & getScriptListenerForKey( const OUString& sScriptName );
+ css::uno::Reference< css::script::XScriptEventsSupplier > getFakeVbaEventsSupplier( const css::uno::Reference< css::awt::XControl>& xControl, OUString const & sCodeName );
+ void nestedAttachEvents( const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& Objects, const css::uno::Any& Helper, OUString& sDialogCodeName );
+ void attachEventsToControl( const css::uno::Reference< css::awt::XControl>& xControl, const css::uno::Reference< css::script::XScriptEventsSupplier >& events, const css::uno::Any& Helper );
+ public:
+ DialogEventsAttacherImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::awt::XControl >& xControl,
+ const css::uno::Reference< css::uno::XInterface >& xHandler,
+ const css::uno::Reference< css::beans::XIntrospectionAccess >& xIntrospect,
+ bool bProviderMode,
+ const css::uno::Reference< css::script::XScriptListener >& xRTLListener ,const OUString& sDialogLibName );
+ virtual ~DialogEventsAttacherImpl() override;
+
+ // XScriptEventsAttacher
+ virtual void SAL_CALL attachEvents( const css::uno::Sequence<
+ css::uno::Reference< css::uno::XInterface > >& Objects,
+ const css::uno::Reference<css::script::XScriptListener>&,
+ const css::uno::Any& Helper ) override;
+ };
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::XAllListener > DialogAllListenerImpl_BASE;
+
+
+ class DialogAllListenerImpl : public DialogAllListenerImpl_BASE
+ {
+ private:
+ css::uno::Reference< css::script::XScriptListener > m_xScriptListener;
+ OUString m_sScriptType;
+ OUString m_sScriptCode;
+
+ void firing_impl( const css::script::AllEventObject& Event, css::uno::Any* pRet );
+
+ public:
+ DialogAllListenerImpl( const css::uno::Reference< css::script::XScriptListener >& rxListener,
+ OUString sScriptType, OUString sScriptCode );
+ virtual ~DialogAllListenerImpl() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XAllListener
+ virtual void SAL_CALL firing( const css::script::AllEventObject& Event ) override;
+ virtual css::uno::Any SAL_CALL approveFiring( const css::script::AllEventObject& Event ) override;
+ };
+
+
+
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::XScriptListener > DialogScriptListenerImpl_BASE;
+
+
+ class DialogScriptListenerImpl : public DialogScriptListenerImpl_BASE
+ {
+ protected:
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ virtual void firing_impl( const css::script::ScriptEvent& aScriptEvent, css::uno::Any* pRet ) = 0;
+ public:
+ explicit DialogScriptListenerImpl( css::uno::Reference< css::uno::XComponentContext > xContext ) : m_xContext(std::move( xContext )) {}
+ virtual ~DialogScriptListenerImpl() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // XScriptListener
+ virtual void SAL_CALL firing( const css::script::ScriptEvent& aScriptEvent ) override;
+ virtual css::uno::Any SAL_CALL approveFiring( const css::script::ScriptEvent& aScriptEvent ) override;
+ };
+
+
+} // namespace dlgprov
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/dlgprov/dlgprov.component b/scripting/source/dlgprov/dlgprov.component
new file mode 100644
index 0000000000..35203afb83
--- /dev/null
+++ b/scripting/source/dlgprov/dlgprov.component
@@ -0,0 +1,32 @@
+<?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.scripting.DialogProvider"
+ constructor="scripting_DialogProviderImpl_get_implementation">
+ <service name="com.sun.star.awt.ContainerWindowProvider"/>
+ <service name="com.sun.star.awt.DialogProvider"/>
+ <service name="com.sun.star.awt.DialogProvider2"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.scripting.DialogModelProvider"
+ constructor="scripting_DialogModelProvider_get_implementation">
+ <service name="com.sun.star.awt.UnoControlDialogModelProvider"/>
+ </implementation>
+</component>
diff --git a/scripting/source/dlgprov/dlgprov.cxx b/scripting/source/dlgprov/dlgprov.cxx
new file mode 100644
index 0000000000..32e85900d4
--- /dev/null
+++ b/scripting/source/dlgprov/dlgprov.cxx
@@ -0,0 +1,702 @@
+/* -*- 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 "dlgprov.hxx"
+#include "dlgevtatt.hxx"
+#include <com/sun/star/awt/UnoControlDialog.hpp>
+#include <com/sun/star/awt/Toolkit.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XWindowPeer.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/io/XInputStreamProvider.hpp>
+#include <com/sun/star/resource/XStringResourceSupplier.hpp>
+#include <com/sun/star/resource/XStringResourceManager.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <com/sun/star/uri/XVndSunStarExpandUrl.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sfx2/app.hxx>
+#include <xmlscript/xmldlg_imexp.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <util/MiscUtils.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+using namespace ::com::sun::star;
+using namespace awt;
+using namespace lang;
+using namespace uno;
+using namespace script;
+using namespace beans;
+using namespace document;
+using namespace ::sf_misc;
+
+namespace dlgprov
+{
+
+ Reference< resource::XStringResourceManager > lcl_getStringResourceManager(const Reference< XComponentContext >& i_xContext, std::u16string_view i_sURL)
+ {
+ INetURLObject aInetObj( i_sURL );
+ OUString aDlgName = aInetObj.GetBase();
+ aInetObj.removeSegment();
+ OUString aDlgLocation = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ css::lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+
+ Reference< task::XInteractionHandler > xDummyHandler;
+
+ Sequence<Any> aArgs{ Any(aDlgLocation),
+ Any(true), // bReadOnly
+ Any(aLocale),
+ Any(aDlgName),
+ Any(OUString()),
+ Any(xDummyHandler) };
+
+ Reference< XMultiComponentFactory > xSMgr_( i_xContext->getServiceManager(), UNO_SET_THROW );
+ // TODO: Ctor
+ Reference< resource::XStringResourceManager > xStringResourceManager( xSMgr_->createInstanceWithContext
+ ( "com.sun.star.resource.StringResourceWithLocation",
+ i_xContext ), UNO_QUERY );
+ if( xStringResourceManager.is() )
+ {
+ Reference< XInitialization > xInit( xStringResourceManager, UNO_QUERY );
+ if( xInit.is() )
+ xInit->initialize( aArgs );
+ }
+ return xStringResourceManager;
+ }
+ Reference< container::XNameContainer > lcl_createControlModel(const Reference< XComponentContext >& i_xContext)
+ {
+ Reference< XMultiComponentFactory > xSMgr_( i_xContext->getServiceManager(), UNO_SET_THROW );
+ Reference< container::XNameContainer > xControlModel( xSMgr_->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", i_xContext ), UNO_QUERY_THROW );
+ return xControlModel;
+ }
+ Reference< container::XNameContainer > lcl_createDialogModel( const Reference< XComponentContext >& i_xContext,
+ const Reference< io::XInputStream >& xInput,
+ const Reference< frame::XModel >& xModel,
+ const Reference< resource::XStringResourceManager >& xStringResourceManager,
+ const Any &aDialogSourceURL)
+ {
+ Reference< container::XNameContainer > xDialogModel( lcl_createControlModel(i_xContext) );
+
+ Reference< beans::XPropertySet > xDlgPropSet( xDialogModel, UNO_QUERY );
+ xDlgPropSet->setPropertyValue( "DialogSourceURL", aDialogSourceURL );
+
+ // #TODO we really need to detect the source of the Dialog, is it
+ // the dialog. E.g. if the dialog was created from basic ( then we just
+ // can't tell where its from )
+ // If we are happy to always substitute the form model for the awt
+ // one then maybe the presence of a document model is enough to trigger
+ // swapping out the models ( or perhaps we only want to do this
+ // for vba mode ) there are a number of feasible and valid possibilities
+ ::xmlscript::importDialogModel( xInput, xDialogModel, i_xContext, xModel );
+
+ // Set resource property
+ if( xStringResourceManager.is() )
+ {
+ Reference< beans::XPropertySet > xDlgPSet( xDialogModel, UNO_QUERY );
+ Any aStringResourceManagerAny;
+ aStringResourceManagerAny <<= xStringResourceManager;
+ xDlgPSet->setPropertyValue( "ResourceResolver", aStringResourceManagerAny );
+ }
+
+ return xDialogModel;
+ }
+
+ // mutex
+
+
+ ::osl::Mutex& getMutex()
+ {
+ static ::osl::Mutex s_aMutex;
+
+ return s_aMutex;
+ }
+
+
+ // DialogProviderImpl
+
+
+ DialogProviderImpl::DialogProviderImpl( const Reference< XComponentContext >& rxContext )
+ :m_xContext( rxContext )
+ {
+ }
+
+
+ DialogProviderImpl::~DialogProviderImpl()
+ {
+ }
+
+
+ static Reference< resource::XStringResourceManager > getStringResourceFromDialogLibrary
+ ( const Reference< container::XNameContainer >& xDialogLib )
+ {
+ Reference< resource::XStringResourceManager > xStringResourceManager;
+ if( xDialogLib.is() )
+ {
+ Reference< resource::XStringResourceSupplier > xStringResourceSupplier( xDialogLib, UNO_QUERY );
+ if( xStringResourceSupplier.is() )
+ {
+ Reference< resource::XStringResourceResolver >
+ xStringResourceResolver = xStringResourceSupplier->getStringResource();
+
+ xStringResourceManager =
+ Reference< resource::XStringResourceManager >( xStringResourceResolver, UNO_QUERY );
+ }
+ }
+ return xStringResourceManager;
+ }
+
+ Reference< container::XNameContainer > DialogProviderImpl::createDialogModel(
+ const Reference< io::XInputStream >& xInput,
+ const Reference< resource::XStringResourceManager >& xStringResourceManager,
+ const Any &aDialogSourceURL)
+ {
+ return lcl_createDialogModel(m_xContext,xInput,m_xModel,xStringResourceManager,aDialogSourceURL);
+ }
+
+ Reference< XControlModel > DialogProviderImpl::createDialogModelForBasic()
+ {
+ if (!m_BasicInfo)
+ // shouldn't get here
+ throw RuntimeException("No information to create dialog" );
+ Reference< resource::XStringResourceManager > xStringResourceManager = getStringResourceFromDialogLibrary( m_BasicInfo->mxDlgLib );
+
+ Any aDialogSourceURL((OUString()));
+ Reference< XControlModel > xCtrlModel( createDialogModel( m_BasicInfo->mxInput, xStringResourceManager, aDialogSourceURL ), UNO_QUERY_THROW );
+ return xCtrlModel;
+ }
+
+ Reference< XControlModel > DialogProviderImpl::createDialogModel( const OUString& sURL )
+ {
+
+ OUString aURL( sURL );
+
+ // parse URL
+ // TODO: use URL parsing class
+ // TODO: decoding of location
+
+ Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) );
+
+ // i75778: Support non-script URLs
+ Reference< io::XInputStream > xInput;
+ Reference< container::XNameContainer > xDialogLib;
+
+ // Accept file URL to single dialog
+ bool bSingleDialog = false;
+
+ Reference< util::XMacroExpander > xMacroExpander =
+ util::theMacroExpander::get(m_xContext);
+
+ Reference< uri::XUriReference > uriRef;
+ for (;;)
+ {
+ uriRef = xFac->parse( aURL );
+ if ( !uriRef.is() )
+ {
+ OUString errorMsg = "DialogProviderImpl::getDialogModel: failed to parse URI: " + aURL;
+ throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 1 );
+ }
+ Reference < uri::XVndSunStarExpandUrl > sxUri( uriRef, UNO_QUERY );
+ if( !sxUri.is() )
+ break;
+
+ aURL = sxUri->expand( xMacroExpander );
+ }
+
+ Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY );
+ if( !sfUri.is() )
+ {
+ bSingleDialog = true;
+
+ // Try any other URL with SimpleFileAccess
+ Reference< ucb::XSimpleFileAccess3 > xSFI = ucb::SimpleFileAccess::create(m_xContext);
+
+ try
+ {
+ xInput = xSFI->openFileRead( aURL );
+ }
+ catch( Exception& )
+ {}
+ }
+ else
+ {
+ OUString sDescription = sfUri->getName();
+
+ sal_Int32 nIndex = 0;
+
+ OUString sLibName = sDescription.getToken( 0, '.', nIndex );
+ OUString sDlgName;
+ if ( nIndex != -1 )
+ sDlgName = sDescription.getToken( 0, '.', nIndex );
+
+ OUString sLocation = sfUri->getParameter( "location" );
+
+
+ // get dialog library container
+ // TODO: dialogs in packages
+ Reference< XLibraryContainer > xLibContainer;
+
+ if ( sLocation == "application" )
+ {
+ xLibContainer = SfxGetpApp()->GetDialogContainer();
+ }
+ else if ( sLocation == "document" )
+ {
+ Reference< XEmbeddedScripts > xDocumentScripts( m_xModel, UNO_QUERY );
+ if ( xDocumentScripts.is() )
+ {
+ xLibContainer = xDocumentScripts->getDialogLibraries();
+ OSL_ENSURE( xLibContainer.is(),
+ "DialogProviderImpl::createDialogModel: invalid dialog container!" );
+ }
+ }
+ else
+ {
+ const Sequence< OUString > aOpenDocsTdocURLs( MiscUtils::allOpenTDocUrls( m_xContext ) );
+ for ( auto const & tdocURL : aOpenDocsTdocURLs )
+ {
+ Reference< frame::XModel > xModel( MiscUtils::tDocUrlToModel( tdocURL ) );
+ OSL_ENSURE( xModel.is(), "DialogProviderImpl::createDialogModel: invalid document model!" );
+ if ( !xModel.is() )
+ continue;
+
+ OUString sDocURL = xModel->getURL();
+ if ( sDocURL.isEmpty() )
+ {
+ sDocURL = ::comphelper::NamedValueCollection::getOrDefault( xModel->getArgs(), u"Title", sDocURL );
+ }
+
+ if ( sLocation != sDocURL )
+ continue;
+
+ Reference< XEmbeddedScripts > xDocumentScripts( m_xModel, UNO_QUERY );
+ if ( !xDocumentScripts.is() )
+ continue;
+
+ xLibContainer = xDocumentScripts->getDialogLibraries();
+ OSL_ENSURE( xLibContainer.is(),
+ "DialogProviderImpl::createDialogModel: invalid dialog container!" );
+ }
+ }
+
+ // get input stream provider
+ Reference< io::XInputStreamProvider > xISP;
+ if ( !xLibContainer.is() )
+ {
+ throw IllegalArgumentException(
+ "DialogProviderImpl::getDialog: library container not found!",
+ Reference< XInterface >(), 1 );
+ }
+
+ // load dialog library
+ if ( !xLibContainer->isLibraryLoaded( sLibName ) )
+ xLibContainer->loadLibrary( sLibName );
+
+ // get dialog library
+ if ( xLibContainer->hasByName( sLibName ) )
+ {
+ Any aElement = xLibContainer->getByName( sLibName );
+ aElement >>= xDialogLib;
+ }
+
+ if ( !xDialogLib.is() )
+ {
+ throw IllegalArgumentException(
+ "DialogProviderImpl::getDialogModel: library not found!",
+ Reference< XInterface >(), 1 );
+ }
+
+ // get input stream provider
+ if ( xDialogLib->hasByName( sDlgName ) )
+ {
+ Any aElement = xDialogLib->getByName( sDlgName );
+ aElement >>= xISP;
+ }
+
+ if ( !xISP.is() )
+ {
+ throw IllegalArgumentException(
+ "DialogProviderImpl::getDialogModel: dialog not found!",
+ Reference< XInterface >(), 1 );
+ }
+
+
+
+ if ( xISP.is() )
+ xInput = xISP->createInputStream();
+ msDialogLibName = sLibName;
+ }
+
+ // import dialog model
+ Reference< XControlModel > xCtrlModel;
+ if ( xInput.is() && m_xContext.is() )
+ {
+ Reference< resource::XStringResourceManager > xStringResourceManager;
+ if( bSingleDialog )
+ {
+ xStringResourceManager = lcl_getStringResourceManager(m_xContext,aURL);
+ }
+ else if( xDialogLib.is() )
+ {
+ xStringResourceManager = getStringResourceFromDialogLibrary( xDialogLib );
+ }
+
+ Any aDialogSourceURLAny;
+ aDialogSourceURLAny <<= aURL;
+
+ Reference< container::XNameContainer > xDialogModel( createDialogModel( xInput , xStringResourceManager, aDialogSourceURLAny ), UNO_SET_THROW);
+
+ xCtrlModel.set( xDialogModel, UNO_QUERY );
+ }
+ return xCtrlModel;
+ }
+
+
+ Reference< XUnoControlDialog > DialogProviderImpl::createDialogControl
+ ( const Reference< XControlModel >& rxDialogModel, const Reference< XWindowPeer >& xParent )
+ {
+ OSL_ENSURE( rxDialogModel.is(), "DialogProviderImpl::getDialogControl: no dialog model" );
+
+ Reference< XUnoControlDialog > xDialogControl;
+
+ if ( m_xContext.is() )
+ {
+ xDialogControl = UnoControlDialog::create( m_xContext );
+
+ // set the model
+ if ( rxDialogModel.is() )
+ xDialogControl->setModel( rxDialogModel );
+
+ // set visible
+ xDialogControl->setVisible( false );
+
+ // get the parent of the dialog control
+ Reference< XWindowPeer > xPeer;
+ if( xParent.is() )
+ {
+ xPeer = xParent;
+ }
+ else if ( m_xModel.is() )
+ {
+ Reference< frame::XController > xController = m_xModel->getCurrentController();
+ if ( xController.is() )
+ {
+ Reference< frame::XFrame > xFrame = xController->getFrame();
+ if ( xFrame.is() )
+ xPeer.set( xFrame->getContainerWindow(), UNO_QUERY );
+ }
+ }
+
+ // create a peer
+ Reference< XToolkit> xToolkit( Toolkit::create( m_xContext ), UNO_QUERY_THROW );
+ xDialogControl->createPeer( xToolkit, xPeer );
+ }
+
+ return xDialogControl;
+ }
+
+
+ void DialogProviderImpl::attachControlEvents(
+ const Reference< XControl >& rxControl,
+ const Reference< XInterface >& rxHandler,
+ const Reference< XIntrospectionAccess >& rxIntrospectionAccess,
+ bool bDialogProviderMode )
+ {
+ if ( !rxControl.is() )
+ return;
+
+ Reference< XControlContainer > xControlContainer( rxControl, UNO_QUERY );
+
+ if ( !xControlContainer.is() )
+ return;
+
+ Sequence< Reference< XControl > > aControls = xControlContainer->getControls();
+ const Reference< XControl >* pControls = aControls.getConstArray();
+ sal_Int32 nControlCount = aControls.getLength();
+
+ Sequence< Reference< XInterface > > aObjects( nControlCount + 1 );
+ Reference< XInterface >* pObjects = aObjects.getArray();
+ for ( sal_Int32 i = 0; i < nControlCount; ++i )
+ {
+ pObjects[i].set( pControls[i], UNO_QUERY );
+ }
+
+ // also add the dialog control itself to the sequence
+ pObjects[nControlCount].set( rxControl, UNO_QUERY );
+
+ Reference<XScriptEventsAttacher> xScriptEventsAttacher
+ = new DialogEventsAttacherImpl(
+ m_xContext, m_xModel, rxControl, rxHandler, rxIntrospectionAccess,
+ bDialogProviderMode,
+ (m_BasicInfo ? m_BasicInfo->mxBasicRTLListener : nullptr), msDialogLibName);
+
+ Any aHelper;
+ xScriptEventsAttacher->attachEvents( aObjects, Reference< XScriptListener >(), aHelper );
+ }
+
+ Reference< XIntrospectionAccess > DialogProviderImpl::inspectHandler( const Reference< XInterface >& rxHandler )
+ {
+ Reference< XIntrospectionAccess > xIntrospectionAccess;
+ static Reference< XIntrospection > xIntrospection;
+
+ if( !rxHandler.is() )
+ return xIntrospectionAccess;
+
+ if( !xIntrospection.is() )
+ {
+ // Get introspection service
+ xIntrospection = theIntrospection::get( m_xContext );
+ }
+
+ // Do introspection
+ try
+ {
+ Any aHandlerAny;
+ aHandlerAny <<= rxHandler;
+ xIntrospectionAccess = xIntrospection->inspect( aHandlerAny );
+ }
+ catch( RuntimeException& )
+ {
+ xIntrospectionAccess.clear();
+ }
+ return xIntrospectionAccess;
+ }
+
+
+ // XServiceInfo
+
+
+ OUString DialogProviderImpl::getImplementationName( )
+ {
+ return "com.sun.star.comp.scripting.DialogProvider";
+ }
+
+ sal_Bool DialogProviderImpl::supportsService( const OUString& rServiceName )
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ Sequence< OUString > DialogProviderImpl::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.awt.DialogProvider",
+ "com.sun.star.awt.DialogProvider2",
+ "com.sun.star.awt.ContainerWindowProvider" };
+ }
+
+
+ // XInitialization
+
+
+ void DialogProviderImpl::initialize( const Sequence< Any >& aArguments )
+ {
+ ::osl::MutexGuard aGuard( getMutex() );
+
+ if ( aArguments.getLength() == 1 )
+ {
+ aArguments[0] >>= m_xModel;
+
+ if ( !m_xModel.is() )
+ {
+ throw RuntimeException( "DialogProviderImpl::initialize: invalid argument format!" );
+ }
+ }
+ else if ( aArguments.getLength() == 4 )
+ {
+ // call from RTL_Impl_CreateUnoDialog
+ aArguments[0] >>= m_xModel;
+ m_BasicInfo.reset( new BasicRTLParams );
+ m_BasicInfo->mxInput.set( aArguments[ 1 ], UNO_QUERY_THROW );
+ // allow null mxDlgLib, a document dialog instantiated from
+ // from application basic is unable to provide ( or find ) it's
+ // Library
+ aArguments[ 2 ] >>= m_BasicInfo->mxDlgLib;
+ // leave the possibility to optionally allow the old dialog creation
+ // to use the new XScriptListener ( which converts the old style macro
+ // to a SF url )
+ m_BasicInfo->mxBasicRTLListener.set( aArguments[ 3 ], UNO_QUERY);
+ }
+ else if ( aArguments.getLength() > 4 )
+ {
+ throw RuntimeException( "DialogProviderImpl::initialize: invalid number of arguments!" );
+ }
+ }
+
+
+ // XDialogProvider
+
+
+ constexpr OUString aDecorationPropName = u"Decoration"_ustr;
+
+ Reference < XControl > DialogProviderImpl::createDialogImpl(
+ const OUString& URL, const Reference< XInterface >& xHandler,
+ const Reference< XWindowPeer >& xParent, bool bDialogProviderMode )
+ {
+ // if the dialog is located in a document, the document must already be open!
+
+ ::osl::MutexGuard aGuard( getMutex() );
+
+
+ // m_xHandler = xHandler;
+
+ //Reference< XDialog > xDialog;
+ Reference< XControl > xCtrl;
+ Reference< XControlModel > xCtrlMod;
+ try
+ {
+ // add support for basic RTL_FUNCTION
+ if (m_BasicInfo)
+ xCtrlMod = createDialogModelForBasic();
+ else
+ {
+ OSL_ENSURE( !URL.isEmpty(), "DialogProviderImpl::getDialog: no URL!" );
+ xCtrlMod = createDialogModel( URL );
+ }
+ }
+ catch ( const RuntimeException& ) { throw; }
+ catch ( const Exception& )
+ {
+ const Any aError( ::cppu::getCaughtException() );
+ throw WrappedTargetRuntimeException( OUString(), *this, aError );
+ }
+ if ( xCtrlMod.is() )
+ {
+ // i83963 Force decoration
+ if( bDialogProviderMode )
+ {
+ uno::Reference< beans::XPropertySet > xDlgModPropSet( xCtrlMod, uno::UNO_QUERY );
+ if( xDlgModPropSet.is() )
+ {
+ try
+ {
+ bool bDecoration = true;
+ Any aDecorationAny = xDlgModPropSet->getPropertyValue( aDecorationPropName );
+ aDecorationAny >>= bDecoration;
+ if( !bDecoration )
+ {
+ xDlgModPropSet->setPropertyValue( aDecorationPropName, Any( true ) );
+ xDlgModPropSet->setPropertyValue( "Title", Any( OUString() ) );
+ }
+ }
+ catch( UnknownPropertyException& )
+ {}
+ }
+ }
+
+ xCtrl.set( createDialogControl( xCtrlMod, xParent ) );
+ if ( xCtrl.is() )
+ {
+ Reference< XIntrospectionAccess > xIntrospectionAccess = inspectHandler( xHandler );
+ attachControlEvents( xCtrl, xHandler, xIntrospectionAccess, bDialogProviderMode );
+ }
+ }
+
+ return xCtrl;
+ }
+
+ Reference < XDialog > DialogProviderImpl::createDialog( const OUString& URL )
+ {
+ Reference< XInterface > xDummyHandler;
+ Reference< XWindowPeer > xDummyPeer;
+ Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xDummyHandler, xDummyPeer, true );
+ Reference< XDialog > xDialog( xControl, UNO_QUERY );
+ return xDialog;
+ }
+
+ Reference < XDialog > DialogProviderImpl::createDialogWithHandler(
+ const OUString& URL, const Reference< XInterface >& xHandler )
+ {
+ if( !xHandler.is() )
+ {
+ throw IllegalArgumentException(
+ "DialogProviderImpl::createDialogWithHandler: Invalid xHandler!",
+ Reference< XInterface >(), 1 );
+ }
+ Reference< XWindowPeer > xDummyPeer;
+ Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xHandler, xDummyPeer, true );
+ Reference< XDialog > xDialog( xControl, UNO_QUERY );
+ return xDialog;
+ }
+
+ Reference < XDialog > DialogProviderImpl::createDialogWithArguments(
+ const OUString& URL, const Sequence< NamedValue >& Arguments )
+ {
+ ::comphelper::NamedValueCollection aArguments( Arguments );
+
+ Reference< XWindowPeer > xParentPeer;
+ if ( aArguments.has( "ParentWindow" ) )
+ {
+ const Any& aParentWindow( aArguments.get( "ParentWindow" ) );
+ if ( !( aParentWindow >>= xParentPeer ) )
+ {
+ const Reference< XControl > xParentControl( aParentWindow, UNO_QUERY );
+ if ( xParentControl.is() )
+ xParentPeer = xParentControl->getPeer();
+ }
+ }
+
+ const Reference< XInterface > xHandler( aArguments.get( "EventHandler" ), UNO_QUERY );
+
+ Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xHandler, xParentPeer, true );
+ Reference< XDialog > xDialog( xControl, UNO_QUERY );
+ return xDialog;
+ }
+
+ Reference< XWindow > DialogProviderImpl::createContainerWindow(
+ const OUString& URL, const OUString&,
+ const Reference< XWindowPeer >& xParent, const Reference< XInterface >& xHandler )
+ {
+ if( !xParent.is() )
+ {
+ throw IllegalArgumentException(
+ "DialogProviderImpl::createContainerWindow: Invalid xParent!",
+ Reference< XInterface >(), 1 );
+ }
+ Reference < XControl > xControl = DialogProviderImpl::createDialogImpl( URL, xHandler, xParent, false );
+ Reference< XWindow> xWindow( xControl, UNO_QUERY );
+ return xWindow;
+ }
+
+
+ // component operations
+
+
+ extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ scripting_DialogProviderImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+ {
+ return cppu::acquire(new DialogProviderImpl(context));
+ }
+
+} // namespace dlgprov
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/dlgprov/dlgprov.hxx b/scripting/source/dlgprov/dlgprov.hxx
new file mode 100644
index 0000000000..0a5d4483a7
--- /dev/null
+++ b/scripting/source/dlgprov/dlgprov.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XDialog.hpp>
+#include <com/sun/star/awt/XDialogProvider2.hpp>
+#include <com/sun/star/awt/XContainerWindowProvider.hpp>
+#include <com/sun/star/awt/XUnoControlDialog.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/resource/XStringResourceManager.hpp>
+#include <com/sun/star/script/XScriptListener.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <osl/mutex.hxx>
+#include <memory>
+
+
+namespace dlgprov
+{
+
+
+ // mutex
+
+
+ ::osl::Mutex& getMutex();
+
+
+
+ css::uno::Reference< css::container::XNameContainer > lcl_createControlModel(const css::uno::Reference< css::uno::XComponentContext >& i_xContext);
+ css::uno::Reference< css::resource::XStringResourceManager > lcl_getStringResourceManager(const css::uno::Reference< css::uno::XComponentContext >& i_xContext, std::u16string_view i_sURL);
+ /// @throws css::uno::Exception
+ css::uno::Reference< css::container::XNameContainer > lcl_createDialogModel(
+ const css::uno::Reference< css::uno::XComponentContext >& i_xContext,
+ const css::uno::Reference< css::io::XInputStream >& xInput,
+ const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager,
+ const css::uno::Any &aDialogSourceURL);
+
+ typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::awt::XDialogProvider2,
+ css::awt::XContainerWindowProvider > DialogProviderImpl_BASE;
+
+ class DialogProviderImpl : public DialogProviderImpl_BASE
+ {
+ private:
+ struct BasicRTLParams
+ {
+ css::uno::Reference< css::io::XInputStream > mxInput;
+ css::uno::Reference< css::container::XNameContainer > mxDlgLib;
+ css::uno::Reference< css::script::XScriptListener > mxBasicRTLListener;
+ };
+ std::unique_ptr< BasicRTLParams > m_BasicInfo;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::frame::XModel > m_xModel;
+
+ OUString msDialogLibName;
+ css::uno::Reference< css::awt::XControlModel > createDialogModel( const OUString& sURL );
+
+ css::uno::Reference< css::awt::XUnoControlDialog > createDialogControl(
+ const css::uno::Reference< css::awt::XControlModel >& rxDialogModel,
+ const css::uno::Reference< css::awt::XWindowPeer >& xParent );
+
+ void attachControlEvents( const css::uno::Reference< css::awt::XControl >& rxControlContainer,
+ const css::uno::Reference< css::uno::XInterface >& rxHandler,
+ const css::uno::Reference< css::beans::XIntrospectionAccess >& rxIntrospectionAccess,
+ bool bDialogProviderMode );
+ css::uno::Reference< css::beans::XIntrospectionAccess > inspectHandler(
+ const css::uno::Reference< css::uno::XInterface >& rxHandler );
+ // helper methods
+ /// @throws css::uno::Exception
+ css::uno::Reference< css::container::XNameContainer > createDialogModel(
+ const css::uno::Reference< css::io::XInputStream >& xInput,
+ const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager,
+ const css::uno::Any &aDialogSourceURL);
+ /// @throws css::uno::Exception
+ css::uno::Reference< css::awt::XControlModel > createDialogModelForBasic();
+
+ // XDialogProvider / XDialogProvider2 impl method
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference < css::awt::XControl > createDialogImpl(
+ const OUString& URL,
+ const css::uno::Reference< css::uno::XInterface >& xHandler,
+ const css::uno::Reference< css::awt::XWindowPeer >& xParent,
+ bool bDialogProviderMode );
+
+ public:
+ explicit DialogProviderImpl(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~DialogProviderImpl() 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XDialogProvider
+ virtual css::uno::Reference < css::awt::XDialog > SAL_CALL createDialog(
+ const OUString& URL ) override;
+
+ // XDialogProvider2
+ virtual css::uno::Reference < css::awt::XDialog > SAL_CALL createDialogWithHandler(
+ const OUString& URL,
+ const css::uno::Reference< css::uno::XInterface >& xHandler ) override;
+
+ virtual css::uno::Reference < css::awt::XDialog > SAL_CALL createDialogWithArguments(
+ const OUString& URL,
+ const css::uno::Sequence< css::beans::NamedValue >& Arguments ) override;
+
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createContainerWindow(
+ const OUString& URL, const OUString& WindowType,
+ const css::uno::Reference< css::awt::XWindowPeer >& xParent,
+ const css::uno::Reference< css::uno::XInterface >& xHandler ) override;
+ };
+
+
+} // namespace dlgprov
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/inc/bcholder.hxx b/scripting/source/inc/bcholder.hxx
new file mode 100644
index 0000000000..9f8add31bb
--- /dev/null
+++ b/scripting/source/inc/bcholder.hxx
@@ -0,0 +1,44 @@
+/* -*- 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/interfacecontainer.h>
+
+
+namespace scripting_helper
+{
+
+ class OBroadcastHelperHolder
+ {
+ ::cppu::OBroadcastHelper m_aBHelper;
+
+ public:
+ OBroadcastHelperHolder( ::osl::Mutex& rMutex ) : m_aBHelper( rMutex ) { }
+
+ ::cppu::OBroadcastHelper& GetBroadcastHelper() { return m_aBHelper; }
+ const ::cppu::OBroadcastHelper& GetBroadcastHelper() const { return m_aBHelper; }
+ };
+
+
+} // namespace scripting_helper
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/inc/util/MiscUtils.hxx b/scripting/source/inc/util/MiscUtils.hxx
new file mode 100644
index 0000000000..2ba4492aba
--- /dev/null
+++ b/scripting/source/inc/util/MiscUtils.hxx
@@ -0,0 +1,138 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XTransientDocumentsDocumentContentFactory.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <comphelper/processfactory.hxx>
+
+namespace sf_misc
+{
+
+class MiscUtils
+{
+public:
+
+static css::uno::Sequence< OUString > allOpenTDocUrls( const css::uno::Reference< css::uno::XComponentContext >& xCtx)
+{
+ css::uno::Sequence< OUString > result;
+ try
+ {
+ if ( !xCtx.is() )
+ {
+ return result;
+ }
+ css::uno::Reference < css::ucb::XSimpleFileAccess3 > xSFA( css::ucb::SimpleFileAccess::create(xCtx) );
+ result = xSFA->getFolderContents( "vnd.sun.star.tdoc:/", true );
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ return result;
+}
+
+static OUString xModelToTdocUrl( const css::uno::Reference< css::frame::XModel >& xModel,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext )
+{
+ css::uno::Reference< css::lang::XMultiComponentFactory > xMCF(
+ xContext->getServiceManager() );
+ css::uno::Reference<
+ css::frame::XTransientDocumentsDocumentContentFactory > xDocFac;
+ try
+ {
+ xDocFac.set(xMCF->createInstanceWithContext(
+ "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
+ xContext ),
+ css::uno::UNO_QUERY );
+ }
+ catch ( css::uno::Exception const & )
+ {
+ // handled below
+ }
+
+ if ( xDocFac.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::ucb::XContent > xContent(
+ xDocFac->createDocumentContent( xModel ) );
+ return xContent->getIdentifier()->getContentIdentifier();
+ }
+ catch ( css::lang::IllegalArgumentException const & )
+ {
+ OSL_FAIL( "Invalid document model!" );
+ }
+ }
+
+ OSL_FAIL( "Unable to obtain URL for document model!" );
+ return OUString();
+}
+
+static css::uno::Reference< css::frame::XModel > tDocUrlToModel( const OUString& url )
+{
+ css::uno::Any result;
+
+ try
+ {
+ ::ucbhelper::Content root( url, nullptr, comphelper::getProcessComponentContext() );
+ result = getUCBProperty( root, "DocumentModel" );
+ }
+ catch ( css::ucb::ContentCreationException& )
+ {
+ // carry on, empty value will be returned
+ }
+ catch ( css::uno::RuntimeException& )
+ {
+ // carry on, empty value will be returned
+ }
+
+ css::uno::Reference< css::frame::XModel > xModel(
+ result, css::uno::UNO_QUERY );
+
+ return xModel;
+}
+
+
+static css::uno::Any getUCBProperty( ::ucbhelper::Content& content, OUString const & prop )
+{
+ css::uno::Any result;
+ try
+ {
+ result = content.getPropertyValue( prop );
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ return result;
+}
+
+};
+} // namespace sf_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/protocolhandler/protocolhandler.component b/scripting/source/protocolhandler/protocolhandler.component
new file mode 100644
index 0000000000..959c332c0e
--- /dev/null
+++ b/scripting/source/protocolhandler/protocolhandler.component
@@ -0,0 +1,26 @@
+<?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.ScriptProtocolHandler"
+ constructor="scripting_ScriptProtocolHandler_get_implementation">
+ <service name="com.sun.star.frame.ProtocolHandler"/>
+ </implementation>
+</component>
diff --git a/scripting/source/protocolhandler/scripthandler.cxx b/scripting/source/protocolhandler/scripthandler.cxx
new file mode 100644
index 0000000000..54ca264b32
--- /dev/null
+++ b/scripting/source/protocolhandler/scripthandler.cxx
@@ -0,0 +1,436 @@
+/* -*- 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 "scripthandler.hxx"
+
+#include <com/sun/star/frame/DispatchResultEvent.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
+#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <framework/documentundoguard.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::script::provider;
+using namespace ::com::sun::star::document;
+
+namespace scripting_protocolhandler
+{
+
+void SAL_CALL ScriptProtocolHandler::initialize(
+ const css::uno::Sequence < css::uno::Any >& aArguments )
+{
+ if ( m_bInitialised )
+ {
+ return ;
+ }
+
+ // first argument contains a reference to the frame (may be empty or the desktop,
+ // but usually it's a "real" frame)
+ if ( aArguments.hasElements() && !( aArguments[ 0 ] >>= m_xFrame ) )
+ {
+ throw RuntimeException( "ScriptProtocolHandler::initialize: could not extract reference to the frame" );
+ }
+
+ ENSURE_OR_THROW( m_xContext.is(), "ScriptProtocolHandler::initialize: No Service Manager available" );
+ m_bInitialised = true;
+}
+
+Reference< XDispatch > SAL_CALL ScriptProtocolHandler::queryDispatch(
+ const URL& aURL, const OUString&, sal_Int32 )
+{
+ Reference< XDispatch > xDispatcher;
+ // get scheme of url
+
+ Reference< uri::XUriReferenceFactory > xFac = uri::UriReferenceFactory::create( m_xContext );
+ Reference< uri::XUriReference > uriRef = xFac->parse( aURL.Complete );
+ if ( uriRef.is() )
+ {
+ if ( uriRef->getScheme() == "vnd.sun.star.script" )
+ {
+ xDispatcher = this;
+ }
+ }
+
+ return xDispatcher;
+}
+
+Sequence< Reference< XDispatch > > SAL_CALL
+ScriptProtocolHandler::queryDispatches(
+const Sequence < DispatchDescriptor >& seqDescriptor )
+{
+ sal_Int32 nCount = seqDescriptor.getLength();
+ Sequence< Reference< XDispatch > > lDispatcher( nCount );
+ std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(),
+ [this](const DispatchDescriptor& rDescr) -> Reference<XDispatch> {
+ return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
+ return lDispatcher;
+}
+
+void SAL_CALL ScriptProtocolHandler::dispatchWithNotification(
+ const URL& aURL, const Sequence < PropertyValue >& lArgs,
+ const Reference< XDispatchResultListener >& xListener )
+{
+ if (officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get())
+ return;
+
+ bool bSuccess = false;
+ Any invokeResult;
+ bool bCaughtException = false;
+ Any aException;
+
+ if ( m_bInitialised )
+ {
+ try
+ {
+ css::uno::Reference<css::uri::XUriReferenceFactory> urifac(
+ css::uri::UriReferenceFactory::create(m_xContext));
+ css::uno::Reference<css::uri::XVndSunStarScriptUrlReference> uri(
+ urifac->parse(aURL.Complete), css::uno::UNO_QUERY_THROW);
+ auto const loc = uri->getParameter("location");
+ bool bIsDocumentScript = loc == "document";
+
+ if ( bIsDocumentScript )
+ {
+ // obtain the component for our security check
+ Reference< XEmbeddedScripts > xDocumentScripts;
+ if ( getScriptInvocation() )
+ xDocumentScripts.set( m_xScriptInvocation->getScriptContainer(), UNO_SET_THROW );
+
+ OSL_ENSURE( xDocumentScripts.is(), "ScriptProtocolHandler::dispatchWithNotification: can't do the security check!" );
+ if ( !xDocumentScripts.is() || !xDocumentScripts->getAllowMacroExecution() )
+ {
+ if ( xListener.is() )
+ {
+ css::frame::DispatchResultEvent aEvent(
+ getXWeak(),
+ css::frame::DispatchResultState::FAILURE,
+ invokeResult );
+ try
+ {
+ xListener->dispatchFinished( aEvent ) ;
+ }
+ catch(const RuntimeException &)
+ {
+ TOOLS_WARN_EXCEPTION("scripting",
+ "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
+ "while dispatchFinished with failure of the execution");
+ }
+ }
+ return;
+ }
+ }
+
+ // Creates a ScriptProvider ( if one is not created already )
+ createScriptProvider();
+
+ Reference< provider::XScript > xFunc =
+ m_xScriptProvider->getScript( aURL.Complete );
+ ENSURE_OR_THROW( xFunc.is(),
+ "ScriptProtocolHandler::dispatchWithNotification: validate xFunc - unable to obtain XScript interface" );
+
+
+ Sequence< Any > inArgs;
+ Sequence< Any > outArgs;
+ Sequence< sal_Int16 > outIndex;
+
+ if ( lArgs.hasElements() )
+ {
+ int argCount = 0;
+ for ( const auto& rArg : lArgs )
+ {
+ // Sometimes we get a propertyval with name = "Referer" or "SynchronMode". These
+ // are not actual arguments to be passed to script, but flags describing the
+ // call, so ignore. Who thought that passing such "meta-arguments" mixed in with
+ // real arguments was a good idea?
+ if ( (rArg.Name != "Referer" &&
+ rArg.Name != "SynchronMode") ||
+ rArg.Name.isEmpty() ) //TODO:???
+ {
+ inArgs.realloc( ++argCount );
+ inArgs.getArray()[ argCount - 1 ] = rArg.Value;
+ }
+ }
+ }
+
+ // attempt to protect the document against the script tampering with its Undo Context
+ std::unique_ptr< ::framework::DocumentUndoGuard > pUndoGuard;
+ if ( bIsDocumentScript )
+ pUndoGuard.reset( new ::framework::DocumentUndoGuard( m_xScriptInvocation ) );
+
+ bSuccess = false;
+ while ( !bSuccess )
+ {
+ std::exception_ptr aFirstCaughtException;
+ try
+ {
+ invokeResult = xFunc->invoke( inArgs, outIndex, outArgs );
+ bSuccess = true;
+ }
+ catch( const provider::ScriptFrameworkErrorException& se )
+ {
+ if (!aFirstCaughtException)
+ aFirstCaughtException = std::current_exception();
+
+ if ( se.errorType != provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT )
+ // the only condition which allows us to retry is if there is no method with the
+ // given name/signature
+ std::rethrow_exception(aFirstCaughtException);
+
+ if ( !inArgs.hasElements() )
+ // no chance to retry if we can't strip more in-args
+ std::rethrow_exception(aFirstCaughtException);
+
+ // strip one argument, then retry
+ inArgs.realloc( inArgs.getLength() - 1 );
+ }
+ }
+ }
+ // Office doesn't handle exceptions rethrown here very well, it cores,
+ // all we can is log them and then set fail for the dispatch event!
+ // (if there is a listener of course)
+ catch ( const Exception & e )
+ {
+ aException = ::cppu::getCaughtException();
+
+ invokeResult <<= "ScriptProtocolHandler::dispatch: caught "
+ + aException.getValueTypeName() + ": " + e.Message;
+
+ bCaughtException = true;
+ }
+ }
+ else
+ {
+ invokeResult <<= OUString(
+ "ScriptProtocolHandler::dispatchWithNotification failed, ScriptProtocolHandler not initialised"
+ );
+ }
+
+ if ( bCaughtException )
+ {
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ pFact->ShowAsyncScriptErrorDialog( nullptr, aException );
+ }
+
+ if ( !xListener.is() )
+ return;
+
+ // always call dispatchFinished(), because we didn't load a document but
+ // executed a macro instead!
+ css::frame::DispatchResultEvent aEvent;
+
+ aEvent.Source = getXWeak();
+ aEvent.Result = invokeResult;
+ if ( bSuccess )
+ {
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ }
+ else
+ {
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ }
+
+ try
+ {
+ xListener->dispatchFinished( aEvent ) ;
+ }
+ catch(const RuntimeException &)
+ {
+ TOOLS_WARN_EXCEPTION("scripting",
+ "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
+ "while dispatchFinished" );
+ }
+}
+
+void SAL_CALL ScriptProtocolHandler::dispatch(
+const URL& aURL, const Sequence< PropertyValue >& lArgs )
+{
+ dispatchWithNotification( aURL, lArgs, Reference< XDispatchResultListener >() );
+}
+
+void SAL_CALL ScriptProtocolHandler::addStatusListener(
+const Reference< XStatusListener >&, const URL& )
+{
+ // implement if status is supported
+}
+
+void SAL_CALL ScriptProtocolHandler::removeStatusListener(
+const Reference< XStatusListener >&, const URL& )
+{}
+
+bool
+ScriptProtocolHandler::getScriptInvocation()
+{
+ if ( !m_xScriptInvocation.is() && m_xFrame.is() )
+ {
+ Reference< XController > xController = m_xFrame->getController();
+ if ( xController .is() )
+ {
+ // try to obtain an XScriptInvocationContext interface, preferred from the
+ // mode, then from the controller
+ if ( !m_xScriptInvocation.set( xController->getModel(), UNO_QUERY ) )
+ m_xScriptInvocation.set( xController, UNO_QUERY );
+ }
+ else
+ {
+ if ( m_xFrame.is() )
+ {
+ SfxFrame* pFrame = nullptr;
+ for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) )
+ {
+ if ( pFrame->GetFrameInterface() == m_xFrame )
+ break;
+ }
+ if (SfxObjectShell* pDocShell = pFrame ? pFrame->GetCurrentDocument() : SfxObjectShell::Current())
+ {
+ Reference< XModel > xModel( pDocShell->GetModel() );
+ m_xScriptInvocation.set( xModel, UNO_QUERY );
+ }
+ }
+ }
+ }
+ return m_xScriptInvocation.is();
+}
+
+void ScriptProtocolHandler::createScriptProvider()
+{
+ if ( m_xScriptProvider.is() )
+ return;
+
+ try
+ {
+ // first, ask the component supporting the XScriptInvocationContext interface
+ // (if there is one) for a script provider
+ if ( getScriptInvocation() )
+ {
+ Reference< XScriptProviderSupplier > xSPS( m_xScriptInvocation, UNO_QUERY );
+ if ( xSPS.is() )
+ m_xScriptProvider = xSPS->getScriptProvider();
+ }
+
+ // second, ask the model in our frame
+ if ( !m_xScriptProvider.is() && m_xFrame.is() )
+ {
+ Reference< XController > xController = m_xFrame->getController();
+ if ( xController .is() )
+ {
+ Reference< XScriptProviderSupplier > xSPS( xController->getModel(), UNO_QUERY );
+ if ( xSPS.is() )
+ m_xScriptProvider = xSPS->getScriptProvider();
+ }
+ }
+
+
+ // as a fallback, ask the controller
+ if ( !m_xScriptProvider.is() && m_xFrame.is() )
+ {
+ Reference< XScriptProviderSupplier > xSPS( m_xFrame->getController(), UNO_QUERY );
+ if ( xSPS.is() )
+ m_xScriptProvider = xSPS->getScriptProvider();
+ }
+
+ // if nothing of this is successful, use the master script provider
+ if ( !m_xScriptProvider.is() )
+ {
+ Reference< provider::XScriptProviderFactory > xFac =
+ provider::theMasterScriptProviderFactory::get( m_xContext );
+
+ Any aContext;
+ if ( getScriptInvocation() )
+ aContext <<= m_xScriptInvocation;
+ m_xScriptProvider.set( xFac->createScriptProvider( aContext ), UNO_SET_THROW );
+ }
+ }
+ catch ( const Exception & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "ScriptProtocolHandler::createScriptProvider: " + e.Message,
+ nullptr, anyEx );
+ }
+}
+
+ScriptProtocolHandler::ScriptProtocolHandler( const Reference< css::uno::XComponentContext > & xContext )
+ : m_bInitialised( false ), m_xContext( xContext )
+{
+}
+
+ScriptProtocolHandler::~ScriptProtocolHandler()
+{
+}
+
+/* XServiceInfo */
+OUString SAL_CALL ScriptProtocolHandler::getImplementationName( )
+{
+ return "com.sun.star.comp.ScriptProtocolHandler";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL ScriptProtocolHandler::supportsService(const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL ScriptProtocolHandler::getSupportedServiceNames()
+{
+ return {"com.sun.star.frame.ProtocolHandler"};
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_ScriptProtocolHandler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ScriptProtocolHandler(context));
+}
+
+} // namespace scripting_protocolhandler
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/protocolhandler/scripthandler.hxx b/scripting/source/protocolhandler/scripthandler.hxx
new file mode 100644
index 0000000000..74667b5a4e
--- /dev/null
+++ b/scripting/source/protocolhandler/scripthandler.hxx
@@ -0,0 +1,114 @@
+/* -*- 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/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+
+
+namespace com::sun::star {
+
+ namespace document {
+ class XScriptInvocationContext;
+ }
+ namespace uno {
+ class Any;
+ class XComponentContext;
+ }
+ namespace lang {
+ class XMultiServiceFactory;
+ class XSingleServiceFactory;
+ }
+ namespace frame {
+ class XFrame;
+ class XDispatch;
+ class XNotifyingDispatch;
+ class XDispatchResultListener;
+ struct DispatchDescriptor;
+ }
+ namespace beans {
+ struct PropertyValue;
+ }
+ namespace util {
+ struct URL;
+ }
+}
+
+namespace scripting_protocolhandler
+{
+
+class ScriptProtocolHandler :
+public ::cppu::WeakImplHelper< css::frame::XDispatchProvider,
+ css::frame::XNotifyingDispatch, css::lang::XServiceInfo, css::lang::XInitialization >
+{
+private:
+ bool m_bInitialised;
+ css::uno::Reference < css::uno::XComponentContext > m_xContext;
+ css::uno::Reference < css::frame::XFrame > m_xFrame;
+ css::uno::Reference < css::script::provider::XScriptProvider > m_xScriptProvider;
+ css::uno::Reference< css::document::XScriptInvocationContext > m_xScriptInvocation;
+
+ void createScriptProvider();
+ bool getScriptInvocation();
+
+public:
+ explicit ScriptProtocolHandler( const css::uno::Reference < css::uno::XComponentContext >& xContext );
+ virtual ~ScriptProtocolHandler() override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence < OUString > SAL_CALL getSupportedServiceNames() override;
+
+ /* Implementation for XDispatchProvider */
+ virtual css::uno::Reference < css::frame::XDispatch > SAL_CALL
+ queryDispatch( const css::util::URL& aURL, const OUString& sTargetFrameName,
+ sal_Int32 eSearchFlags ) override ;
+ virtual css::uno::Sequence< css::uno::Reference < css::frame::XDispatch > > SAL_CALL
+ queryDispatches(
+ const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescriptor ) override;
+
+ /* Implementation for X(Notifying)Dispatch */
+ virtual void SAL_CALL dispatchWithNotification(
+ const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& Listener ) override;
+ virtual void SAL_CALL dispatch(
+ const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) override;
+ virtual void SAL_CALL addStatusListener(
+ const css::uno::Reference< css::frame::XStatusListener >& xControl,
+ const css::util::URL& aURL ) override;
+ virtual void SAL_CALL removeStatusListener(
+ const css::uno::Reference< css::frame::XStatusListener >& xControl,
+ const css::util::URL& aURL ) override;
+
+ /* Implementation for XInitialization */
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence < css::uno::Any >& aArguments ) override;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/ActiveMSPList.cxx b/scripting/source/provider/ActiveMSPList.cxx
new file mode 100644
index 0000000000..fbd2b2b053
--- /dev/null
+++ b/scripting/source/provider/ActiveMSPList.cxx
@@ -0,0 +1,295 @@
+/* -*- 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 <cppuhelper/exc_hlp.hxx>
+#include <util/MiscUtils.hxx>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+
+#include "ActiveMSPList.hxx"
+
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::script;
+using namespace ::sf_misc;
+
+namespace func_provider
+{
+
+ActiveMSPList::ActiveMSPList( const Reference< XComponentContext > & xContext ) : m_xContext( xContext )
+{
+ userDirString = "user";
+ shareDirString = "share";
+ bundledDirString = "bundled";
+}
+
+ActiveMSPList::~ActiveMSPList()
+{
+}
+
+Reference< provider::XScriptProvider >
+ActiveMSPList::createNewMSP( const uno::Any& context )
+{
+ Sequence< Any > args( &context, 1 );
+
+ Reference< provider::XScriptProvider > msp(
+ m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.script.provider.MasterScriptProvider", args, m_xContext ), UNO_QUERY );
+ return msp;
+}
+
+class NonDocMSPCreator
+{
+public:
+ explicit NonDocMSPCreator(ActiveMSPList *pList)
+ {
+ pList->createNonDocMSPs();
+ }
+};
+
+namespace
+{
+ //thread-safe method to ensure createNonDocMSPs is called once
+ void ensureNonDocMSPs(ActiveMSPList *pList)
+ {
+ static NonDocMSPCreator theCreator(pList);
+ }
+}
+
+Reference< provider::XScriptProvider >
+ActiveMSPList::getMSPFromAnyContext( const Any& aContext )
+{
+ Reference< provider::XScriptProvider > msp;
+ OUString sContext;
+ if ( aContext >>= sContext )
+ {
+ msp = getMSPFromStringContext( sContext );
+ return msp;
+ }
+
+ Reference< frame::XModel > xModel( aContext, UNO_QUERY );
+
+ Reference< document::XScriptInvocationContext > xScriptContext( aContext, UNO_QUERY );
+ if ( xScriptContext.is() )
+ {
+ try
+ {
+ // the component supports executing scripts embedded in a - possibly foreign document.
+ // Check whether this other document it's the component itself.
+ if ( !xModel.is() || ( xModel != xScriptContext->getScriptContainer() ) )
+ {
+ msp = getMSPFromInvocationContext( xScriptContext );
+ return msp;
+ }
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ xModel.set( Reference< frame::XModel >() );
+ }
+ }
+
+ if ( xModel.is() )
+ {
+ sContext = MiscUtils::xModelToTdocUrl( xModel, m_xContext );
+ msp = getMSPFromStringContext( sContext );
+ return msp;
+ }
+
+ ensureNonDocMSPs(this);
+ return m_hMsps[ shareDirString ];
+}
+
+Reference< provider::XScriptProvider >
+ ActiveMSPList::getMSPFromInvocationContext( const Reference< document::XScriptInvocationContext >& xContext )
+{
+ Reference< provider::XScriptProvider > msp;
+
+ Reference< document::XEmbeddedScripts > xScripts;
+ if ( xContext.is() )
+ xScripts.set( xContext->getScriptContainer() );
+ if ( !xScripts.is() )
+ {
+ throw lang::IllegalArgumentException(
+ "Failed to create MasterScriptProvider for ScriptInvocationContext: "
+ "Component supporting XEmbeddScripts interface not found.",
+ nullptr, 1 );
+ }
+
+ ::osl::MutexGuard guard( m_mutex );
+
+ Reference< XInterface > xNormalized( xContext, UNO_QUERY );
+ ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized );
+ if ( pos == m_mScriptComponents.end() )
+ {
+ // TODO
+ msp = createNewMSP( uno::Any( xContext ) );
+ addActiveMSP( xNormalized, msp );
+ }
+ else
+ {
+ msp = pos->second;
+ }
+
+ return msp;
+}
+
+Reference< provider::XScriptProvider >
+ ActiveMSPList::getMSPFromStringContext( const OUString& context )
+{
+ Reference< provider::XScriptProvider > msp;
+ try
+ {
+ if ( context.startsWith( "vnd.sun.star.tdoc" ) )
+ {
+ Reference< frame::XModel > xModel( MiscUtils::tDocUrlToModel( context ) );
+
+ Reference< document::XEmbeddedScripts > xScripts( xModel, UNO_QUERY );
+ Reference< document::XScriptInvocationContext > xScriptsContext( xModel, UNO_QUERY );
+ if ( !xScripts.is() && !xScriptsContext.is() )
+ {
+ throw lang::IllegalArgumentException(
+ "Failed to create MasterScriptProvider for '"
+ + context +
+ "': Either XEmbeddScripts or XScriptInvocationContext need to be supported by the document.",
+ nullptr, 1 );
+ }
+
+ ::osl::MutexGuard guard( m_mutex );
+ Reference< XInterface > xNormalized( xModel, UNO_QUERY );
+ ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized );
+ if ( pos == m_mScriptComponents.end() )
+ {
+ msp = createNewMSP( context );
+ addActiveMSP( xNormalized, msp );
+ }
+ else
+ {
+ msp = pos->second;
+ }
+ }
+ else
+ {
+ ::osl::MutexGuard guard( m_mutex );
+ Msp_hash::iterator h_itEnd = m_hMsps.end();
+ Msp_hash::const_iterator itr = m_hMsps.find( context );
+ if ( itr == h_itEnd )
+ {
+ msp = createNewMSP( context );
+ m_hMsps[ context ] = msp;
+ }
+ else
+ {
+ msp = m_hMsps[ context ];
+ }
+ }
+ }
+ catch( const lang::IllegalArgumentException& )
+ {
+ // allowed to leave
+ }
+ catch( const RuntimeException& )
+ {
+ // allowed to leave
+ }
+ catch( const Exception& )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException(
+ "Failed to create MasterScriptProvider for context '"
+ + context + "'.",
+ *this, anyEx );
+ }
+ return msp;
+}
+
+void
+ActiveMSPList::addActiveMSP( const Reference< uno::XInterface >& xComponent,
+ const Reference< provider::XScriptProvider >& msp )
+{
+ ::osl::MutexGuard guard( m_mutex );
+ Reference< XInterface > xNormalized( xComponent, UNO_QUERY );
+ ScriptComponent_map::const_iterator pos = m_mScriptComponents.find( xNormalized );
+ if ( pos != m_mScriptComponents.end() )
+ return;
+
+ m_mScriptComponents[ xNormalized ] = msp;
+
+ // add self as listener for component disposal
+ // should probably throw from this method!!, reexamine
+ try
+ {
+ Reference< lang::XComponent > xBroadcaster( xComponent, UNO_QUERY_THROW );
+ xBroadcaster->addEventListener( this );
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+}
+
+
+void SAL_CALL ActiveMSPList::disposing( const css::lang::EventObject& Source )
+
+{
+ try
+ {
+ Reference< XInterface > xNormalized( Source.Source, UNO_QUERY );
+ if ( xNormalized.is() )
+ {
+ ::osl::MutexGuard guard( m_mutex );
+ ScriptComponent_map::iterator pos = m_mScriptComponents.find( xNormalized );
+ if ( pos != m_mScriptComponents.end() )
+ m_mScriptComponents.erase( pos );
+ }
+ }
+ catch ( const Exception& )
+ {
+ // if we get an exception here, there is not much we can do about
+ // it can't throw as it will screw up the model that is calling dispose
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+}
+
+void
+ActiveMSPList::createNonDocMSPs()
+{
+ // do creation of user and share MSPs here
+ OUString serviceName("com.sun.star.script.provider.MasterScriptProvider");
+
+ Sequence< Any > args{ Any(userDirString) };
+ Reference< provider::XScriptProvider > userMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY );
+ // should check if provider reference is valid
+ m_hMsps[ userDirString ] = userMsp;
+
+ args = { Any(shareDirString) };
+ Reference< provider::XScriptProvider > shareMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY );
+ // should check if provider reference is valid
+ m_hMsps[ shareDirString ] = shareMsp;
+
+ args = { Any(bundledDirString) };
+ Reference< provider::XScriptProvider > bundledMsp( m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( serviceName, args, m_xContext ), UNO_QUERY );
+ // should check if provider reference is valid
+ m_hMsps[ bundledDirString ] = bundledMsp;
+}
+
+} // namespace func_provider
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/ActiveMSPList.hxx b/scripting/source/provider/ActiveMSPList.hxx
new file mode 100644
index 0000000000..fb52629c1d
--- /dev/null
+++ b/scripting/source/provider/ActiveMSPList.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 <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XEventListener.hpp>
+
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <map>
+#include <unordered_map>
+
+namespace func_provider
+{
+
+//Typedefs
+typedef std::map < css::uno::Reference< css::uno::XInterface >
+ , css::uno::Reference< css::script::provider::XScriptProvider >
+ > ScriptComponent_map;
+
+typedef std::unordered_map< OUString,
+ css::uno::Reference< css::script::provider::XScriptProvider > > Msp_hash;
+
+class NonDocMSPCreator;
+
+class ActiveMSPList : public ::cppu::WeakImplHelper< css::lang::XEventListener >
+{
+
+public:
+
+ explicit ActiveMSPList( const css::uno::Reference<
+ css::uno::XComponentContext > & xContext );
+ virtual ~ActiveMSPList() override;
+
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ getMSPFromStringContext( const OUString& context );
+
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ getMSPFromAnyContext( const css::uno::Any& context );
+
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ getMSPFromInvocationContext( const css::uno::Reference< css::document::XScriptInvocationContext >& context );
+
+ //XEventListener
+
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+private:
+ void addActiveMSP( const css::uno::Reference< css::uno::XInterface >& xComponent,
+ const css::uno::Reference< css::script::provider::XScriptProvider >& msp );
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ createNewMSP( const css::uno::Any& context );
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ createNewMSP( const OUString& context )
+ {
+ return createNewMSP( css::uno::Any( context ) );
+ }
+
+ friend class NonDocMSPCreator;
+ void createNonDocMSPs();
+
+ Msp_hash m_hMsps;
+ ScriptComponent_map m_mScriptComponents;
+ osl::Mutex m_mutex;
+ OUString userDirString;
+ OUString shareDirString;
+ OUString bundledDirString;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+};
+} // func_provider
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/BrowseNodeFactoryImpl.cxx b/scripting/source/provider/BrowseNodeFactoryImpl.cxx
new file mode 100644
index 0000000000..3ea45aeecd
--- /dev/null
+++ b/scripting/source/provider/BrowseNodeFactoryImpl.cxx
@@ -0,0 +1,650 @@
+/* -*- 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 <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/reflection/ProxyFactory.hpp>
+
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/script/browse/BrowseNodeFactoryViewTypes.hpp>
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+
+#include <comphelper/diagnose_ex.hxx>
+
+#include "BrowseNodeFactoryImpl.hxx"
+#include <util/MiscUtils.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <memory>
+#include <optional>
+#include <string_view>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+using namespace ::sf_misc;
+
+namespace browsenodefactory
+{
+namespace {
+class BrowseNodeAggregator :
+ public ::cppu::WeakImplHelper< browse::XBrowseNode >
+{
+private:
+ OUString m_Name;
+ std::vector< Reference< browse::XBrowseNode > > m_Nodes;
+
+public:
+
+ explicit BrowseNodeAggregator( const Reference< browse::XBrowseNode >& node )
+ : m_Name(node->getName())
+ {
+ m_Nodes.resize( 1 );
+ m_Nodes[ 0 ] = node;
+ }
+
+ void addBrowseNode( const Reference< browse::XBrowseNode>& node )
+ {
+ m_Nodes.push_back( node );
+ }
+
+ virtual OUString
+ SAL_CALL getName() override
+ {
+ return m_Name;
+ }
+
+ virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
+ getChildNodes() override
+ {
+ std::vector< Sequence< Reference < browse::XBrowseNode > > > seqs;
+ seqs.reserve( m_Nodes.size() );
+
+ sal_Int32 numChildren = 0;
+
+ for (Reference<XBrowseNode> & xNode : m_Nodes)
+ {
+ Sequence< Reference < browse::XBrowseNode > > children;
+ try
+ {
+ children = xNode->getChildNodes();
+ seqs.push_back( children );
+ numChildren += children.getLength();
+ }
+ catch ( Exception& )
+ {
+ // some form of exception getting child nodes so they
+ // won't be displayed
+ }
+ }
+
+ Sequence< Reference < browse::XBrowseNode > > result( numChildren );
+ sal_Int32 index = 0;
+ for ( const Sequence< Reference < browse::XBrowseNode > >& children : seqs )
+ {
+ std::copy(children.begin(), children.end(), std::next(result.getArray(), index));
+ index += children.getLength();
+
+ if (index >= numChildren)
+ break;
+ }
+ return result;
+ }
+
+ virtual sal_Bool SAL_CALL
+ hasChildNodes() override
+ {
+ for (Reference<XBrowseNode> & xNode : m_Nodes)
+ {
+ try
+ {
+ if ( xNode->hasChildNodes() )
+ {
+ return true;
+ }
+ }
+ catch ( Exception& )
+ {
+ // some form of exception getting child nodes so move
+ // on to the next one
+ }
+ }
+
+ return false;
+ }
+
+ virtual sal_Int16 SAL_CALL getType() override
+ {
+ return browse::BrowseNodeTypes::CONTAINER;
+ }
+};
+
+struct alphaSort
+{
+ bool operator()( std::u16string_view a, std::u16string_view b )
+ {
+ return a.compare( b ) < 0;
+ }
+};
+class LocationBrowseNode :
+ public ::cppu::WeakImplHelper< browse::XBrowseNode >
+{
+private:
+ std::optional<std::unordered_map< OUString, Reference< browse::XBrowseNode > >> m_hBNA;
+ std::vector< OUString > m_vStr;
+ OUString m_sNodeName;
+ Reference< browse::XBrowseNode > m_origNode;
+
+public:
+
+ explicit LocationBrowseNode( const Reference< browse::XBrowseNode >& node )
+ : m_sNodeName(node->getName())
+ {
+ m_origNode.set( node );
+ }
+
+
+ // XBrowseNode
+
+ virtual OUString SAL_CALL getName() override
+ {
+ return m_sNodeName;
+ }
+
+ virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
+ getChildNodes() override
+ {
+ if ( !m_hBNA )
+ {
+ loadChildNodes();
+ }
+
+ Sequence< Reference< browse::XBrowseNode > > children( m_hBNA->size() );
+ auto childrenRange = asNonConstRange(children);
+ sal_Int32 index = 0;
+
+ for ( const auto& str : m_vStr )
+ {
+ childrenRange[ index ].set( m_hBNA->find( str )->second );
+ ++index;
+ }
+
+ return children;
+ }
+
+ virtual sal_Bool SAL_CALL hasChildNodes() override
+ {
+ return true;
+ }
+
+ virtual sal_Int16 SAL_CALL getType() override
+ {
+ return browse::BrowseNodeTypes::CONTAINER;
+ }
+
+private:
+
+ void loadChildNodes()
+ {
+ m_hBNA.emplace();
+
+ const Sequence< Reference< browse::XBrowseNode > > langNodes =
+ m_origNode->getChildNodes();
+
+ for ( const auto& rLangNode : langNodes )
+ {
+ Reference< browse::XBrowseNode > xbn;
+ if ( rLangNode->getName() == "uno_packages" )
+ {
+ xbn.set( new LocationBrowseNode( rLangNode ) );
+ }
+ else
+ {
+ xbn.set( rLangNode );
+ }
+
+ const Sequence< Reference< browse::XBrowseNode > > grandchildren =
+ xbn->getChildNodes();
+
+ for ( const Reference< browse::XBrowseNode >& grandchild : grandchildren )
+ {
+ auto h_it =
+ m_hBNA->find( grandchild->getName() );
+
+ if ( h_it != m_hBNA->end() )
+ {
+ BrowseNodeAggregator* bna = static_cast< BrowseNodeAggregator* >( h_it->second.get() );
+ bna->addBrowseNode( grandchild );
+ }
+ else
+ {
+ Reference< browse::XBrowseNode > bna(
+ new BrowseNodeAggregator( grandchild ) );
+ (*m_hBNA)[ grandchild->getName() ].set( bna );
+ m_vStr.push_back( grandchild->getName() );
+ }
+ }
+ }
+ // sort children alphabetically
+ ::std::sort( m_vStr.begin(), m_vStr.end(), alphaSort() );
+ }
+};
+
+std::vector< Reference< browse::XBrowseNode > > getAllBrowseNodes( const Reference< XComponentContext >& xCtx )
+{
+ const Sequence< OUString > openDocs =
+ MiscUtils::allOpenTDocUrls( xCtx );
+
+ Reference< provider::XScriptProviderFactory > xFac;
+ sal_Int32 initialSize = openDocs.getLength() + 2;
+ sal_Int32 mspIndex = 0;
+
+ std::vector< Reference < browse::XBrowseNode > > locnBNs( initialSize );
+ try
+ {
+ xFac = provider::theMasterScriptProviderFactory::get( xCtx );
+
+ locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( OUString("user") ) ), UNO_QUERY_THROW );
+ locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( OUString("share") ) ), UNO_QUERY_THROW );
+ }
+ // TODO proper exception handling, should throw
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("scripting", "Caught" );
+ locnBNs.resize( mspIndex );
+ return locnBNs;
+ }
+
+ for ( const auto& rDoc : openDocs )
+ {
+ try
+ {
+ Reference< frame::XModel > model( MiscUtils::tDocUrlToModel( rDoc ), UNO_SET_THROW );
+
+ // #i44599 Check if it's a real document or something special like Hidden/Preview
+ css::uno::Reference< css::frame::XController > xCurrentController = model->getCurrentController();
+ if( xCurrentController.is() )
+ {
+ utl::MediaDescriptor aMD( model->getArgs() );
+ bool bDefault = false;
+ bool bHidden = aMD.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_HIDDEN, bDefault );
+ bool bPreview = aMD.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_PREVIEW, bDefault );
+ if( !bHidden && !bPreview )
+ {
+ Reference< document::XEmbeddedScripts > xScripts( model, UNO_QUERY );
+ if ( xScripts.is() )
+ locnBNs[ mspIndex++ ].set( xFac->createScriptProvider( Any( model ) ), UNO_QUERY_THROW );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+
+ }
+
+ std::vector< Reference < browse::XBrowseNode > > locnBNs_Return( mspIndex );
+ for ( sal_Int32 j = 0; j < mspIndex; j++ )
+ locnBNs_Return[j] = locnBNs[j];
+
+ return locnBNs_Return;
+}
+
+} // namespace
+
+typedef ::std::vector< Reference< browse::XBrowseNode > > vXBrowseNodes;
+
+namespace {
+
+struct alphaSortForBNodes
+{
+ bool operator()( const Reference< browse::XBrowseNode >& a, const Reference< browse::XBrowseNode >& b )
+ {
+ return a->getName().compareTo( b->getName() ) < 0;
+ }
+};
+
+}
+
+typedef ::cppu::WeakImplHelper< browse::XBrowseNode > t_BrowseNodeBase;
+
+namespace {
+
+class DefaultBrowseNode :
+ public t_BrowseNodeBase
+{
+
+private:
+ Reference< browse::XBrowseNode > m_xWrappedBrowseNode;
+ Reference< lang::XTypeProvider > m_xWrappedTypeProv;
+ Reference< XAggregation > m_xAggProxy;
+ Reference< XComponentContext > m_xCtx;
+
+public:
+ DefaultBrowseNode( const Reference< XComponentContext >& xCtx, const Reference< browse::XBrowseNode>& xNode ) : m_xWrappedBrowseNode( xNode ), m_xWrappedTypeProv( xNode, UNO_QUERY ), m_xCtx( xCtx )
+ {
+ OSL_ENSURE( m_xWrappedBrowseNode.is(), "DefaultBrowseNode::DefaultBrowseNode(): No BrowseNode to wrap" );
+ OSL_ENSURE( m_xWrappedTypeProv.is(), "DefaultBrowseNode::DefaultBrowseNode(): No BrowseNode to wrap" );
+ OSL_ENSURE( m_xCtx.is(), "DefaultBrowseNode::DefaultBrowseNode(): No ComponentContext" );
+ // Use proxy factory service to create aggregatable proxy.
+ try
+ {
+ Reference< reflection::XProxyFactory > xProxyFac =
+ reflection::ProxyFactory::create( m_xCtx );
+ m_xAggProxy = xProxyFac->createProxy( m_xWrappedBrowseNode );
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "scripting", "DefaultBrowseNode::DefaultBrowseNode" );
+ }
+ OSL_ENSURE( m_xAggProxy.is(),
+ "DefaultBrowseNode::DefaultBrowseNode: Wrapped BrowseNode cannot be aggregated!" );
+
+ if ( !m_xAggProxy.is() )
+ return;
+
+ osl_atomic_increment( &m_refCount );
+
+ /* i35609 - Fix crash on Solaris. The setDelegator call needs
+ to be in its own block to ensure that all temporary Reference
+ instances that are acquired during the call are released
+ before m_refCount is decremented again */
+ {
+ m_xAggProxy->setDelegator(
+ getXWeak() );
+ }
+
+ osl_atomic_decrement( &m_refCount );
+ }
+
+ virtual ~DefaultBrowseNode() override
+ {
+ if ( m_xAggProxy.is() )
+ {
+ m_xAggProxy->setDelegator( uno::Reference< uno::XInterface >() );
+ }
+ }
+
+ virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
+ getChildNodes() override
+ {
+ if ( hasChildNodes() )
+ {
+ vXBrowseNodes aVNodes;
+ const Sequence < Reference< browse::XBrowseNode > > nodes =
+ m_xWrappedBrowseNode->getChildNodes();
+ for ( const Reference< browse::XBrowseNode >& xBrowseNode : nodes )
+ {
+ OSL_ENSURE( xBrowseNode.is(), "DefaultBrowseNode::getChildNodes(): Invalid BrowseNode" );
+ if( xBrowseNode.is() )
+ aVNodes.push_back( new DefaultBrowseNode( m_xCtx, xBrowseNode ) );
+ }
+
+ ::std::sort( aVNodes.begin(), aVNodes.end(), alphaSortForBNodes() );
+ Sequence < Reference< browse::XBrowseNode > > children( aVNodes.size() );
+ auto childrenRange = asNonConstRange(children);
+ sal_Int32 i = 0;
+ for ( const auto& rxNode : aVNodes )
+ {
+ childrenRange[ i ].set( rxNode );
+ i++;
+ }
+ return children;
+ }
+ else
+ {
+ // no nodes
+
+ Sequence < Reference< browse::XBrowseNode > > none;
+ return none;
+ }
+ }
+
+ virtual sal_Int16 SAL_CALL getType() override
+ {
+ return m_xWrappedBrowseNode->getType();
+ }
+
+ virtual OUString
+ SAL_CALL getName() override
+ {
+ return m_xWrappedBrowseNode->getName();
+ }
+
+ virtual sal_Bool SAL_CALL
+ hasChildNodes() override
+ {
+ return m_xWrappedBrowseNode->hasChildNodes();
+ }
+
+ // XInterface
+ virtual Any SAL_CALL queryInterface( const Type& aType ) override
+ {
+ Any aRet = t_BrowseNodeBase::queryInterface( aType );
+ if ( aRet.hasValue() )
+ {
+ return aRet;
+ }
+ if ( m_xAggProxy.is() )
+ {
+ return m_xAggProxy->queryAggregation( aType );
+ }
+ else
+ {
+ return Any();
+ }
+ }
+
+ // XTypeProvider (implemented by base, but needs to be overridden for
+ // delegating to aggregate)
+ virtual Sequence< Type > SAL_CALL getTypes() override
+ {
+ return m_xWrappedTypeProv->getTypes();
+ }
+ virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+};
+
+class DefaultRootBrowseNode :
+ public ::cppu::WeakImplHelper< browse::XBrowseNode >
+{
+
+private:
+ vXBrowseNodes m_vNodes;
+ OUString m_Name;
+
+public:
+ explicit DefaultRootBrowseNode( const Reference< XComponentContext >& xCtx )
+ {
+ std::vector< Reference< browse::XBrowseNode > > nodes =
+ getAllBrowseNodes( xCtx );
+
+ for (Reference< browse::XBrowseNode > & xNode : nodes)
+ {
+ m_vNodes.push_back( new DefaultBrowseNode( xCtx, xNode ) );
+ }
+ m_Name = "Root";
+ }
+
+ virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
+ getChildNodes() override
+ {
+ // no need to sort user, share, doc1...docN
+ //::std::sort( m_vNodes.begin(), m_vNodes.end(), alphaSortForBNodes() );
+ Sequence < Reference< browse::XBrowseNode > > children( m_vNodes.size() );
+ auto childrenRange = asNonConstRange(children);
+ sal_Int32 i = 0;
+ for ( const auto& rxNode : m_vNodes )
+ {
+ childrenRange[ i ].set( rxNode );
+ i++;
+ }
+ return children;
+ }
+
+ virtual sal_Int16 SAL_CALL getType() override
+ {
+ return browse::BrowseNodeTypes::ROOT;
+ }
+
+ virtual OUString
+ SAL_CALL getName() override
+ {
+ return m_Name;
+ }
+
+ virtual sal_Bool SAL_CALL
+ hasChildNodes() override
+ {
+ bool result = true;
+ if ( m_vNodes.empty() )
+ {
+ result = false;
+ }
+ return result;
+ }
+};
+
+
+class SelectorBrowseNode :
+ public ::cppu::WeakImplHelper< browse::XBrowseNode >
+{
+private:
+ Reference< XComponentContext > m_xComponentContext;
+
+public:
+ explicit SelectorBrowseNode( const Reference< XComponentContext >& xContext )
+ : m_xComponentContext( xContext )
+ {
+ }
+
+ virtual OUString SAL_CALL getName() override
+ {
+ return "Root";
+ }
+
+ virtual Sequence< Reference< browse::XBrowseNode > > SAL_CALL
+ getChildNodes() override
+ {
+
+ std::vector< Reference < browse::XBrowseNode > > locnBNs = getAllBrowseNodes( m_xComponentContext );
+
+ Sequence< Reference< browse::XBrowseNode > > children(
+ locnBNs.size() );
+ auto childrenRange = asNonConstRange(children);
+
+ for ( size_t j = 0; j < locnBNs.size(); j++ )
+ {
+ childrenRange[j] = new LocationBrowseNode( locnBNs[j] );
+ }
+
+ return children;
+ }
+
+ virtual sal_Bool SAL_CALL hasChildNodes() override
+ {
+ return true; // will always be user and share
+ }
+
+ virtual sal_Int16 SAL_CALL getType() override
+ {
+ return browse::BrowseNodeTypes::CONTAINER;
+ }
+};
+
+}
+
+BrowseNodeFactoryImpl::BrowseNodeFactoryImpl(
+ Reference< XComponentContext > const & xComponentContext )
+ : m_xComponentContext( xComponentContext )
+{
+}
+
+BrowseNodeFactoryImpl::~BrowseNodeFactoryImpl()
+{
+}
+
+
+// Implementation of XBrowseNodeFactory
+
+
+/*
+ * The selector hierarchy is the standard hierarchy for organizers with the
+ * language nodes removed.
+ */
+Reference< browse::XBrowseNode > SAL_CALL
+BrowseNodeFactoryImpl::createView( sal_Int16 viewType )
+{
+ switch( viewType )
+ {
+ case browse::BrowseNodeFactoryViewTypes::MACROSELECTOR:
+ return new SelectorBrowseNode( m_xComponentContext );
+ case browse::BrowseNodeFactoryViewTypes::MACROORGANIZER:
+ return getOrganizerHierarchy();
+ default:
+ throw RuntimeException( "Unknown view type" );
+ }
+}
+
+Reference< browse::XBrowseNode >
+BrowseNodeFactoryImpl::getOrganizerHierarchy() const
+{
+ Reference< browse::XBrowseNode > xRet = new DefaultRootBrowseNode( m_xComponentContext );
+ return xRet;
+}
+
+// Implementation of XServiceInfo
+
+
+OUString SAL_CALL
+BrowseNodeFactoryImpl::getImplementationName()
+{
+ return "com.sun.star.script.browse.BrowseNodeFactory";
+}
+
+Sequence< OUString > SAL_CALL
+BrowseNodeFactoryImpl::getSupportedServiceNames()
+{
+ return { "com.sun.star.script.browse.BrowseNodeFactory" };
+}
+
+sal_Bool BrowseNodeFactoryImpl::supportsService(OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_BrowseNodeFactoryImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new BrowseNodeFactoryImpl(context));
+}
+
+} // namespace browsenodefactory
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/BrowseNodeFactoryImpl.hxx b/scripting/source/provider/BrowseNodeFactoryImpl.hxx
new file mode 100644
index 0000000000..9085680210
--- /dev/null
+++ b/scripting/source/provider/BrowseNodeFactoryImpl.hxx
@@ -0,0 +1,70 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+#include <com/sun/star/script/browse/XBrowseNodeFactory.hpp>
+
+namespace browsenodefactory
+{
+
+class BrowseNodeFactoryImpl :
+ public ::cppu::WeakImplHelper <
+ css::script::browse::XBrowseNodeFactory,
+ css::lang::XServiceInfo >
+{
+private:
+ css::uno::Reference< css::uno::XComponentContext > m_xComponentContext;
+
+protected:
+ virtual ~BrowseNodeFactoryImpl() override;
+
+public:
+ explicit BrowseNodeFactoryImpl(
+ css::uno::Reference< css::uno::XComponentContext > const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL
+ supportsService( OUString const & serviceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XBrowseNodeFactory
+ virtual css::uno::Reference< css::script::browse::XBrowseNode > SAL_CALL
+ createView( sal_Int16 viewType ) override;
+private:
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::script::browse::XBrowseNode >
+ getOrganizerHierarchy() const;
+};
+
+
+} // namespace browsenodefactory
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/MasterScriptProvider.cxx b/scripting/source/provider/MasterScriptProvider.cxx
new file mode 100644
index 0000000000..66fc58db78
--- /dev/null
+++ b/scripting/source/provider/MasterScriptProvider.cxx
@@ -0,0 +1,676 @@
+/* -*- 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 <comphelper/SetFlagContextHelper.hxx>
+#include <comphelper/documentinfo.hxx>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/urlobj.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
+
+#include <util/MiscUtils.hxx>
+#include <sal/log.hxx>
+
+#include "MasterScriptProvider.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::document;
+using namespace ::sf_misc;
+
+namespace func_provider
+{
+
+static bool endsWith( std::u16string_view target, std::u16string_view item )
+{
+ size_t index = target.find( item );
+ return index != std::u16string_view::npos &&
+ index == ( target.size() - item.size() );
+}
+
+/* should be available in some central location. */
+
+// XScriptProvider implementation
+
+
+MasterScriptProvider::MasterScriptProvider( const Reference< XComponentContext > & xContext ):
+ m_xContext( xContext ), m_bIsValid( false ), m_bInitialised( false ),
+ m_bIsPkgMSP( false )
+{
+ ENSURE_OR_THROW( m_xContext.is(), "MasterScriptProvider::MasterScriptProvider: No context available\n" );
+ m_xMgr = m_xContext->getServiceManager();
+ ENSURE_OR_THROW( m_xMgr.is(), "MasterScriptProvider::MasterScriptProvider: No service manager available\n" );
+ m_bIsValid = true;
+}
+
+
+MasterScriptProvider::~MasterScriptProvider()
+{
+}
+
+
+void SAL_CALL MasterScriptProvider::initialize( const Sequence < Any >& args )
+{
+ if ( m_bInitialised )
+ return;
+
+ m_bIsValid = false;
+
+ sal_Int32 len = args.getLength();
+ if ( len > 1 )
+ {
+ throw RuntimeException(
+ "MasterScriptProvider::initialize: invalid number of arguments" );
+ }
+
+ Sequence< Any > invokeArgs( len );
+
+ if ( len != 0 )
+ {
+ auto pinvokeArgs = invokeArgs.getArray();
+ // check if first parameter is a string
+ // if it is, this implies that this is a MSP created
+ // with a user or share ctx ( used for browse functionality )
+
+ if ( args[ 0 ] >>= m_sCtxString )
+ {
+ pinvokeArgs[ 0 ] = args[ 0 ];
+ if ( m_sCtxString.startsWith( "vnd.sun.star.tdoc" ) )
+ {
+ m_xModel = MiscUtils::tDocUrlToModel( m_sCtxString );
+ }
+ }
+ else if ( args[ 0 ] >>= m_xInvocationContext )
+ {
+ m_xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY_THROW );
+ }
+ else
+ {
+ args[ 0 ] >>= m_xModel;
+ }
+
+ if ( m_xModel.is() )
+ {
+ // from the arguments, we were able to deduce a model. That alone doesn't
+ // suffice, we also need an XEmbeddedScripts which actually indicates support
+ // for embedding scripts
+ Reference< XEmbeddedScripts > xScripts( m_xModel, UNO_QUERY );
+ if ( !xScripts.is() )
+ {
+ throw lang::IllegalArgumentException(
+ "The given document does not support embedding scripts into it, and cannot be associated with such a document.",
+ *this,
+ 1
+ );
+ }
+
+ try
+ {
+ m_sCtxString = MiscUtils::xModelToTdocUrl( m_xModel, m_xContext );
+ }
+ catch ( const Exception& )
+ {
+ Any aError( ::cppu::getCaughtException() );
+
+ Exception aException;
+ aError >>= aException;
+ OUString buf =
+ "MasterScriptProvider::initialize: caught " +
+ aError.getValueTypeName() +
+ ":" +
+ aException.Message;
+ throw lang::WrappedTargetException( buf, *this, aError );
+ }
+
+ if ( m_xInvocationContext.is() && m_xInvocationContext != m_xModel )
+ pinvokeArgs[ 0 ] <<= m_xInvocationContext;
+ else
+ pinvokeArgs[ 0 ] <<= m_sCtxString;
+ }
+
+ OUString pkgSpec = "uno_packages";
+ sal_Int32 indexOfPkgSpec = m_sCtxString.lastIndexOf( pkgSpec );
+
+ // if context string ends with "uno_packages"
+ if ( indexOfPkgSpec > -1 && m_sCtxString.match( pkgSpec, indexOfPkgSpec ) )
+ {
+ m_bIsPkgMSP = true;
+ }
+ else
+ {
+ m_bIsPkgMSP = false;
+ }
+ }
+ else // no args
+ {
+ // use either scripting context or maybe zero args?
+ invokeArgs = Sequence< Any >( 0 ); // no arguments
+ }
+ m_sAargs = invokeArgs;
+ // don't create pkg mgr MSP for documents, not supported
+ if ( !m_bIsPkgMSP && !m_xModel.is() )
+ {
+ createPkgProvider();
+ }
+
+ m_bInitialised = true;
+ m_bIsValid = true;
+}
+
+
+void MasterScriptProvider::createPkgProvider()
+{
+ try
+ {
+ Any location;
+ location <<= m_sCtxString + ":uno_packages";
+
+ Reference< provider::XScriptProviderFactory > xFac =
+ provider::theMasterScriptProviderFactory::get( m_xContext );
+
+ m_xMSPPkg.set(
+ xFac->createScriptProvider( location ), UNO_SET_THROW );
+
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("scripting.provider", "Exception creating MasterScriptProvider for uno_packages in context "
+ << m_sCtxString );
+ }
+}
+
+
+Reference< provider::XScript >
+MasterScriptProvider::getScript( const OUString& scriptURI )
+{
+ if ( !m_bIsValid )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "MasterScriptProvider not initialised", Reference< XInterface >(),
+ scriptURI, "",
+ provider::ScriptFrameworkErrorType::UNKNOWN );
+ }
+
+ // need to get the language from the string
+
+ Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) );
+
+ Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI );
+
+ Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY );
+
+ if ( !uriRef.is() || !sfUri.is() )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "Incorrect format for Script URI: " + scriptURI,
+ Reference< XInterface >(),
+ scriptURI, "",
+ provider::ScriptFrameworkErrorType::UNKNOWN );
+ }
+
+ OUString langKey("language");
+ OUString locKey("location");
+
+ if ( !sfUri->hasParameter( langKey ) ||
+ !sfUri->hasParameter( locKey ) ||
+ ( sfUri->getName().isEmpty() ) )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "Incorrect format for Script URI: " + scriptURI,
+ Reference< XInterface >(),
+ scriptURI, "",
+ provider::ScriptFrameworkErrorType::UNKNOWN );
+ }
+
+ OUString language = sfUri->getParameter( langKey );
+ OUString location = sfUri->getParameter( locKey );
+
+ // if script us located in uno pkg
+ sal_Int32 index = -1;
+ OUString pkgTag(":uno_packages");
+ // for languages other than basic, scripts located in uno packages
+ // are merged into the user/share location context.
+ // For other languages the location attribute in script url has the form
+ // location = [user|share]:uno_packages or location :uno_packages/xxxx.uno.pkg
+ // we need to extract the value of location part from the
+ // location attribute of the script, if the script is located in an
+ // uno package then that is the location part up to and including
+ // ":uno_packages", if the script is not in a uno package then the
+ // normal value is used e.g. user or share.
+ // The value extracted will be used to determine if the script is
+ // located in the same location context as this MSP.
+ // For Basic, the language script provider can handle the execution of a
+ // script in any location context
+ if ( ( index = location.indexOf( pkgTag ) ) > -1 )
+ {
+ location = location.copy( 0, index + pkgTag.getLength() );
+ }
+
+ Reference< provider::XScript > xScript;
+
+ // If the script location is in the same location context as this
+ // MSP then delete to the language provider controlled by this MSP
+ // ** Special case is BASIC, all calls to getScript will be handled
+ // by the language script provider in the current location context
+ // even if it's different
+ if ( ( location == "document"
+ && m_xModel.is()
+ )
+ || ( endsWith( m_sCtxString, location ) )
+ || ( language == "Basic" )
+ )
+ {
+ Reference< provider::XScriptProvider > xScriptProvider;
+ OUString serviceName = "com.sun.star.script.provider.ScriptProviderFor" + language;
+ if ( !providerCache() )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ "No LanguageProviders detected",
+ Reference< XInterface >(),
+ sfUri->getName(), language,
+ provider::ScriptFrameworkErrorType::NOTSUPPORTED );
+ }
+
+ try
+ {
+ xScriptProvider.set(
+ providerCache()->getProvider( serviceName ),
+ UNO_SET_THROW );
+ }
+ catch( const Exception& e )
+ {
+ throw provider::ScriptFrameworkErrorException(
+ e.Message, Reference< XInterface >(),
+ sfUri->getName(), language,
+ provider::ScriptFrameworkErrorType::NOTSUPPORTED );
+ }
+
+ xScript=xScriptProvider->getScript( scriptURI );
+ }
+ else
+ {
+ Reference< provider::XScriptProviderFactory > xFac_ =
+ provider::theMasterScriptProviderFactory::get( m_xContext );
+
+ Reference< provider::XScriptProvider > xSP(
+ xFac_->createScriptProvider( Any( location ) ), UNO_SET_THROW );
+ xScript = xSP->getScript( scriptURI );
+ }
+
+ return xScript;
+}
+
+
+ProviderCache*
+MasterScriptProvider::providerCache()
+{
+ if ( !m_pPCache )
+ {
+ std::scoped_lock aGuard( m_mutex );
+ if ( !m_pPCache )
+ {
+ Sequence<OUString> denylist { "com.sun.star.script.provider.ScriptProviderForBasic" };
+
+ if ( !m_bIsPkgMSP )
+ {
+ m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs ) );
+ }
+ else
+ {
+ m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs, denylist ) );
+ }
+ }
+ }
+ return m_pPCache.get();
+}
+
+
+OUString SAL_CALL
+MasterScriptProvider::getName()
+{
+ if ( !m_bIsPkgMSP )
+ {
+ OUString sCtx = getContextString();
+ if ( sCtx.startsWith( "vnd.sun.star.tdoc" ) )
+ {
+ Reference< frame::XModel > xModel = m_xModel;
+ if ( !xModel.is() )
+ {
+ xModel = MiscUtils::tDocUrlToModel( sCtx );
+ }
+
+ m_sNodeName = ::comphelper::DocumentInfo::getDocumentTitle( xModel );
+ }
+ else
+ {
+ m_sNodeName = parseLocationName( getContextString() );
+ }
+ }
+ else
+ {
+ m_sNodeName = "uno_packages";
+ }
+ return m_sNodeName;
+}
+
+
+Sequence< Reference< browse::XBrowseNode > > SAL_CALL
+MasterScriptProvider::getChildNodes()
+{
+ Sequence< Reference< provider::XScriptProvider > > providers = providerCache()->getAllProviders();
+
+ sal_Int32 size = providers.getLength();
+ bool hasPkgs = m_xMSPPkg.is();
+ if ( hasPkgs )
+ {
+ size++;
+ }
+ Sequence< Reference< browse::XBrowseNode > > children( size );
+ auto childrenRange = asNonConstRange(children);
+ sal_Int32 provIndex = 0;
+ for ( ; provIndex < providers.getLength(); provIndex++ )
+ {
+ childrenRange[ provIndex ].set( providers[ provIndex ], UNO_QUERY );
+ }
+
+ if ( hasPkgs )
+ {
+ childrenRange[ provIndex ].set( m_xMSPPkg, UNO_QUERY );
+
+ }
+
+ return children;
+}
+
+
+sal_Bool SAL_CALL
+MasterScriptProvider::hasChildNodes()
+{
+ return true;
+}
+
+
+sal_Int16 SAL_CALL
+MasterScriptProvider::getType()
+{
+ return browse::BrowseNodeTypes::CONTAINER;
+}
+
+
+OUString
+MasterScriptProvider::parseLocationName( const OUString& location )
+{
+ // strip out the last leaf of location name
+ // e.g. file://dir1/dir2/Blah.sxw - > Blah.sxw
+ OUString temp = location;
+ INetURLObject aURLObj( temp );
+ if ( !aURLObj.HasError() )
+ temp = aURLObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ return temp;
+}
+
+namespace
+{
+template <typename Proc> bool FindProviderAndApply(ProviderCache& rCache, Proc p)
+{
+ auto pass = [&rCache, &p]() -> bool
+ {
+ bool bResult = false;
+ const Sequence<Reference<provider::XScriptProvider>> aAllProviders = rCache.getAllProviders();
+ for (const auto& rProv : aAllProviders)
+ {
+ Reference<container::XNameContainer> xCont(rProv, UNO_QUERY);
+ if (!xCont.is())
+ {
+ continue;
+ }
+ try
+ {
+ bResult = p(xCont);
+ if (bResult)
+ break;
+ }
+ catch (const Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("scripting.provider", "ignoring");
+ }
+ }
+ return bResult;
+ };
+ bool bSuccess = false;
+ // 1. Try to perform the operation without trying to enable JVM (if disabled)
+ // This allows us to avoid useless user interaction in case when other provider
+ // (not JVM) actually handles the operation.
+ {
+ css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext());
+ bSuccess = pass();
+ }
+ // 2. Now retry asking to enable JVM in case we didn't succeed first time
+ if (!bSuccess)
+ {
+ bSuccess = pass();
+ }
+ return bSuccess;
+}
+} // namespace
+
+// Register Package
+void SAL_CALL
+MasterScriptProvider::insertByName( const OUString& aName, const Any& aElement )
+{
+ if ( !m_bIsPkgMSP )
+ {
+ if ( !m_xMSPPkg.is() )
+ {
+ throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
+ }
+
+ Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
+ xCont->insertByName( aName, aElement );
+ }
+ else
+ {
+ Reference< deployment::XPackage > xPkg( aElement, UNO_QUERY );
+ if ( !xPkg.is() )
+ {
+ throw lang::IllegalArgumentException( "Couldn't convert to XPackage",
+ Reference < XInterface > (), 2 );
+ }
+ if ( aName.isEmpty() )
+ {
+ throw lang::IllegalArgumentException( "Name not set!!",
+ Reference < XInterface > (), 1 );
+ }
+ // TODO for library package parse the language, for the moment will try
+ // to get each provider to process the new Package, the first one the succeeds
+ // will terminate processing
+ const bool bSuccess = FindProviderAndApply(
+ *providerCache(), [&aName, &aElement](Reference<container::XNameContainer>& xCont) {
+ xCont->insertByName(aName, aElement);
+ return true;
+ });
+ if (!bSuccess)
+ {
+ // No script providers could process the package
+ throw lang::IllegalArgumentException( "Failed to register package for " + aName,
+ Reference < XInterface > (), 2 );
+ }
+ }
+}
+
+
+// Revoke Package
+void SAL_CALL
+MasterScriptProvider::removeByName( const OUString& Name )
+{
+ if ( !m_bIsPkgMSP )
+ {
+ if ( !m_xMSPPkg.is() )
+ {
+ throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
+ }
+
+ Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
+ xCont->removeByName( Name );
+ }
+ else
+ {
+ if ( Name.isEmpty() )
+ {
+ throw lang::IllegalArgumentException( "Name not set!!",
+ Reference < XInterface > (), 1 );
+ }
+ // TODO for Script library package url parse the language,
+ // for the moment will just try to get each provider to process remove/revoke
+ // request, the first one the succeeds will terminate processing
+ const bool bSuccess = FindProviderAndApply(
+ *providerCache(), [&Name](Reference<container::XNameContainer>& xCont) {
+ xCont->removeByName(Name);
+ return true;
+ });
+ if (!bSuccess)
+ {
+ // No script providers could process the package
+ throw lang::IllegalArgumentException( "Failed to revoke package for " + Name,
+ Reference < XInterface > (), 1 );
+ }
+
+ }
+}
+
+
+void SAL_CALL
+MasterScriptProvider::replaceByName( const OUString& /*aName*/, const Any& /*aElement*/ )
+{
+ // TODO needs implementing
+ throw RuntimeException( "replaceByName not implemented!!!!" );
+}
+
+Any SAL_CALL
+MasterScriptProvider::getByName( const OUString& /*aName*/ )
+{
+ // TODO needs to be implemented
+ throw RuntimeException( "getByName not implemented!!!!" );
+}
+
+sal_Bool SAL_CALL
+MasterScriptProvider::hasByName( const OUString& aName )
+{
+ bool result = false;
+ if ( !m_bIsPkgMSP )
+ {
+ if ( m_xMSPPkg.is() )
+ {
+ Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
+ result = xCont->hasByName( aName );
+ }
+ // If this is a document provider then we shouldn't
+ // have a PackageProvider
+ else if (!m_xModel.is())
+ {
+ throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
+ }
+
+ }
+ else
+ {
+ if ( aName.isEmpty() )
+ {
+ throw lang::IllegalArgumentException( "Name not set!!",
+ Reference < XInterface > (), 1 );
+ }
+ // TODO for Script library package url parse the language,
+ // for the moment will just try to get each provider to see if the
+ // package exists in any provider, first one that succeed will
+ // terminate the loop
+ result = FindProviderAndApply(
+ *providerCache(), [&aName](Reference<container::XNameContainer>& xCont) {
+ return xCont->hasByName(aName);
+ });
+ }
+ return result;
+}
+
+
+Sequence< OUString > SAL_CALL
+MasterScriptProvider::getElementNames( )
+{
+ // TODO needs implementing
+ throw RuntimeException( "getElementNames not implemented!!!!" );
+}
+
+Type SAL_CALL
+MasterScriptProvider::getElementType( )
+{
+ // TODO needs implementing
+ Type t;
+ return t;
+}
+
+sal_Bool SAL_CALL MasterScriptProvider::hasElements( )
+{
+ // TODO needs implementing
+ throw RuntimeException( "hasElements not implemented!!!!" );
+}
+
+
+OUString SAL_CALL MasterScriptProvider::getImplementationName( )
+{
+ return "com.sun.star.script.provider.MasterScriptProvider";
+}
+
+sal_Bool SAL_CALL MasterScriptProvider::supportsService( const OUString& serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+
+Sequence< OUString > SAL_CALL MasterScriptProvider::getSupportedServiceNames( )
+{
+ return {
+ "com.sun.star.script.provider.MasterScriptProvider",
+ "com.sun.star.script.browse.BrowseNode",
+ "com.sun.star.script.provider.ScriptProvider" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_MasterScriptProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new MasterScriptProvider(context));
+}
+
+} // namespace func_provider
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/MasterScriptProvider.hxx b/scripting/source/provider/MasterScriptProvider.hxx
new file mode 100644
index 0000000000..0e6c40f5f0
--- /dev/null
+++ b/scripting/source/provider/MasterScriptProvider.hxx
@@ -0,0 +1,131 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/document/XScriptInvocationContext.hpp>
+
+#include <com/sun/star/lang/XInitialization.hpp>
+
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+#include <com/sun/star/script/browse/XBrowseNode.hpp>
+
+#include "ProviderCache.hxx"
+#include <memory>
+#include <mutex>
+
+namespace func_provider
+{
+
+ typedef ::cppu::WeakImplHelper<
+ css::script::provider::XScriptProvider,
+ css::script::browse::XBrowseNode, css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::container::XNameContainer > t_helper;
+
+class MasterScriptProvider :
+ public t_helper
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit MasterScriptProvider(
+ const css::uno::Reference< css::uno::XComponentContext >
+ & xContext );
+ virtual ~MasterScriptProvider() override;
+
+ // XServiceInfo implementation
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ // XBrowseNode implementation
+ virtual OUString SAL_CALL getName() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes() override;
+ virtual sal_Bool SAL_CALL hasChildNodes() override;
+ virtual sal_Int16 SAL_CALL getType() override;
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XScriptProvider implementation
+ virtual css::uno::Reference < css::script::provider::XScript > SAL_CALL
+ getScript( const OUString& scriptURI ) override;
+
+ /**
+ * XInitialise implementation
+ *
+ * @param args expected to contain a single OUString
+ * containing the URI
+ */
+ virtual void SAL_CALL initialize( const css::uno::Sequence < css::uno::Any > & args ) override;
+
+ // returns context string for this provider, eg
+ const OUString& getContextString() const { return m_sCtxString; }
+
+private:
+ static OUString parseLocationName( const OUString& location );
+ void createPkgProvider();
+
+ ProviderCache* providerCache();
+ /* to obtain other services if needed */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::lang::XMultiComponentFactory > m_xMgr;
+ css::uno::Reference< css::frame::XModel > m_xModel;
+ css::uno::Reference< css::document::XScriptInvocationContext > m_xInvocationContext;
+ css::uno::Sequence< css::uno::Any > m_sAargs;
+ OUString m_sNodeName;
+
+ // This component supports XInitialization, it can be created
+ // using createInstanceXXX() or createInstanceWithArgumentsXXX using
+ // the service Manager.
+ // Need to detect proper initialisation and validity
+ // for the object, so m_bIsValid indicates that the object is valid is set in ctor
+ // in case of createInstanceWithArgumentsXXX() called m_bIsValid is set to reset
+ // and then set to true when initialisation is complete
+ bool m_bIsValid;
+ // m_bInitialised ensure initialisation only takes place once.
+ bool m_bInitialised;
+ bool m_bIsPkgMSP;
+ css::uno::Reference< css::script::provider::XScriptProvider > m_xMSPPkg;
+ std::unique_ptr<ProviderCache> m_pPCache;
+ std::mutex m_mutex;
+ OUString m_sCtxString;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/MasterScriptProviderFactory.cxx b/scripting/source/provider/MasterScriptProviderFactory.cxx
new file mode 100644
index 0000000000..acdb8f8da0
--- /dev/null
+++ b/scripting/source/provider/MasterScriptProviderFactory.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 <cppuhelper/supportsservice.hxx>
+
+#include "MasterScriptProviderFactory.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::script;
+
+namespace func_provider
+{
+
+MasterScriptProviderFactory::MasterScriptProviderFactory(
+ Reference< XComponentContext > const & xComponentContext )
+ : m_xComponentContext( xComponentContext )
+{
+}
+
+MasterScriptProviderFactory::~MasterScriptProviderFactory()
+{
+}
+
+Reference< provider::XScriptProvider > SAL_CALL
+MasterScriptProviderFactory::createScriptProvider( const Any& context )
+{
+ Reference< provider::XScriptProvider > xMsp( getActiveMSPList() ->getMSPFromAnyContext( context ), UNO_SET_THROW );
+ return xMsp;
+}
+
+const rtl::Reference< ActiveMSPList > &
+MasterScriptProviderFactory::getActiveMSPList() const
+{
+ if ( !m_MSPList.is() )
+ {
+ ::osl::MutexGuard guard( ::osl::Mutex::getGlobalMutex() );
+ if ( !m_MSPList.is() )
+ m_MSPList = new ActiveMSPList( m_xComponentContext );
+ }
+ return m_MSPList;
+}
+
+OUString SAL_CALL MasterScriptProviderFactory::getImplementationName()
+{
+ return "com.sun.star.script.provider.MasterScriptProviderFactory";
+}
+
+Sequence< OUString > SAL_CALL MasterScriptProviderFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.script.provider.MasterScriptProviderFactory" };
+}
+
+sal_Bool MasterScriptProviderFactory::supportsService(
+ OUString const & serviceName )
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+} // namespace func_provider
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_MasterScriptProviderFactory_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new func_provider::MasterScriptProviderFactory(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/MasterScriptProviderFactory.hxx b/scripting/source/provider/MasterScriptProviderFactory.hxx
new file mode 100644
index 0000000000..9fbc8705db
--- /dev/null
+++ b/scripting/source/provider/MasterScriptProviderFactory.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <com/sun/star/script/provider/XScriptProviderFactory.hpp>
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+
+#include "ActiveMSPList.hxx"
+
+namespace func_provider
+{
+
+class MasterScriptProviderFactory :
+ public ::cppu::WeakImplHelper <
+ css::script::provider::XScriptProviderFactory,
+ css::lang::XServiceInfo >
+{
+private:
+
+ mutable rtl::Reference< ActiveMSPList > m_MSPList;
+
+ const css::uno::Reference< css::uno::XComponentContext > m_xComponentContext;
+
+ const rtl::Reference< ActiveMSPList > & getActiveMSPList() const;
+
+protected:
+ virtual ~MasterScriptProviderFactory() override;
+
+public:
+ explicit MasterScriptProviderFactory(
+ css::uno::Reference< css::uno::XComponentContext > const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL
+ supportsService( OUString const & serviceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XScriptProviderFactory
+ virtual css::uno::Reference< css::script::provider::XScriptProvider >
+ SAL_CALL createScriptProvider( const css::uno::Any& context ) override;
+};
+
+
+} // namespace func_provider
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/ProviderCache.cxx b/scripting/source/provider/ProviderCache.cxx
new file mode 100644
index 0000000000..7b240fe590
--- /dev/null
+++ b/scripting/source/provider/ProviderCache.cxx
@@ -0,0 +1,203 @@
+/* -*- 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 <comphelper/sequence.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include "ProviderCache.hxx"
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::script;
+
+namespace func_provider
+{
+
+ProviderCache::ProviderCache( const Reference< XComponentContext >& xContext, const Sequence< Any >& scriptContext ) : m_Sctx( scriptContext ), m_xContext( xContext )
+{
+ // initialise m_hProviderDetailsCache with details of ScriptProviders
+ // will use createContentEnumeration
+
+ m_xMgr = m_xContext->getServiceManager();
+ ENSURE_OR_THROW( m_xMgr.is(), "ProviderCache::ProviderCache() failed to obtain ServiceManager" );
+ populateCache();
+}
+
+
+ProviderCache::ProviderCache( const Reference< XComponentContext >& xContext, const Sequence< Any >& scriptContext, const Sequence< OUString >& denyList ) : m_sDenyList( denyList ), m_Sctx( scriptContext ), m_xContext( xContext )
+
+{
+ // initialise m_hProviderDetailsCache with details of ScriptProviders
+ // will use createContentEnumeration
+
+ m_xMgr = m_xContext->getServiceManager();
+ ENSURE_OR_THROW( m_xMgr.is(), "ProviderCache::ProviderCache() failed to obtain ServiceManager" );
+ populateCache();
+}
+
+ProviderCache::~ProviderCache()
+{
+}
+
+Reference< provider::XScriptProvider >
+ProviderCache::getProvider( const OUString& providerName )
+{
+ std::scoped_lock aGuard( m_mutex );
+ Reference< provider::XScriptProvider > provider;
+ ProviderDetails_hash::iterator h_it = m_hProviderDetailsCache.find( providerName );
+ if ( h_it != m_hProviderDetailsCache.end() )
+ {
+ if ( h_it->second.provider.is() )
+ {
+ provider = h_it->second.provider;
+ }
+ else
+ {
+ // need to create provider and insert into hash
+ provider = createProvider( h_it->second );
+ }
+ }
+ return provider;
+}
+
+Sequence < Reference< provider::XScriptProvider > >
+ProviderCache::getAllProviders()
+{
+ // need to create providers that haven't been created already
+ // so check what providers exist and what ones don't
+
+ std::scoped_lock aGuard( m_mutex );
+ Sequence < Reference< provider::XScriptProvider > > providers ( m_hProviderDetailsCache.size() );
+ // should assert if size !> 0
+ if ( !m_hProviderDetailsCache.empty() )
+ {
+ auto pproviders = providers.getArray();
+ sal_Int32 providerIndex = 0;
+ for (auto& rDetail : m_hProviderDetailsCache)
+ {
+ Reference<provider::XScriptProvider> xScriptProvider = rDetail.second.provider;
+ if ( xScriptProvider.is() )
+ {
+ pproviders[ providerIndex++ ] = xScriptProvider;
+ }
+ else
+ {
+ // create provider
+ try
+ {
+ xScriptProvider = createProvider(rDetail.second);
+ pproviders[ providerIndex++ ] = xScriptProvider;
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("scripting");
+ }
+ }
+ }
+
+ if (providerIndex < providers.getLength())
+ {
+ providers.realloc( providerIndex );
+ }
+
+ }
+ else
+ {
+ SAL_WARN("scripting", "no available providers, something very wrong!!!");
+ }
+ return providers;
+}
+
+void
+ProviderCache::populateCache()
+{
+ // wrong name in services.rdb
+ OUString serviceName;
+ std::scoped_lock aGuard( m_mutex );
+ try
+ {
+ Reference< container::XContentEnumerationAccess > xEnumAccess( m_xMgr, UNO_QUERY_THROW );
+ Reference< container::XEnumeration > xEnum = xEnumAccess->createContentEnumeration ( "com.sun.star.script.provider.LanguageScriptProvider" );
+
+ while ( xEnum->hasMoreElements() )
+ {
+
+ Reference< lang::XSingleComponentFactory > factory( xEnum->nextElement(), UNO_QUERY_THROW );
+ Reference< lang::XServiceInfo > xServiceInfo( factory, UNO_QUERY_THROW );
+
+ const Sequence< OUString > serviceNames = xServiceInfo->getSupportedServiceNames();
+
+ if ( serviceNames.hasElements() )
+ {
+ auto pName = std::find_if(serviceNames.begin(), serviceNames.end(),
+ [this](const OUString& rName) {
+ return rName.startsWith("com.sun.star.script.provider.ScriptProviderFor")
+ && !isInDenyList(rName);
+ });
+ if (pName != serviceNames.end())
+ {
+ serviceName = *pName;
+ ProviderDetails details;
+ details.factory = factory;
+ m_hProviderDetailsCache[ serviceName ] = details;
+ }
+ }
+ }
+ }
+ catch ( const Exception &e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "ProviderCache::populateCache: couldn't obtain XSingleComponentFactory for " + serviceName
+ + " " + e.Message,
+ nullptr, anyEx );
+ }
+}
+
+Reference< provider::XScriptProvider >
+ProviderCache::createProvider( ProviderDetails& details )
+{
+ try
+ {
+ details.provider.set(
+ details.factory->createInstanceWithArgumentsAndContext( m_Sctx, m_xContext ), UNO_QUERY_THROW );
+ }
+ catch ( const Exception& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "ProviderCache::createProvider() Error creating provider from factory. " + e.Message,
+ nullptr, anyEx );
+ }
+
+ return details.provider;
+}
+
+bool
+ProviderCache::isInDenyList( const OUString& serviceName ) const
+{
+ return comphelper::findValue(m_sDenyList, serviceName) != -1;
+}
+} //end namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/ProviderCache.hxx b/scripting/source/provider/ProviderCache.hxx
new file mode 100644
index 0000000000..b565d1d082
--- /dev/null
+++ b/scripting/source/provider/ProviderCache.hxx
@@ -0,0 +1,80 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+
+#include <com/sun/star/script/provider/XScriptProvider.hpp>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace func_provider
+{
+
+//Typedefs
+
+
+struct ProviderDetails
+{
+ //css::uno::Reference< css::lang::XSingleServiceFactory > factory;
+ css::uno::Reference< css::lang::XSingleComponentFactory > factory;
+ css::uno::Reference< css::script::provider::XScriptProvider > provider;
+};
+typedef std::unordered_map < OUString, ProviderDetails > ProviderDetails_hash;
+
+
+class ProviderCache
+{
+
+public:
+ /// @throws css::uno::RuntimeException
+ ProviderCache( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Sequence< css::uno::Any >& scriptContext );
+ /// @throws css::uno::RuntimeException
+ ProviderCache( const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Sequence< css::uno::Any >& scriptContext,
+ const css::uno::Sequence< OUString >& denyList );
+ ~ProviderCache();
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ getProvider( const OUString& providerName );
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence < css::uno::Reference< css::script::provider::XScriptProvider > >
+ getAllProviders();
+private:
+ /// @throws css::uno::RuntimeException
+ void populateCache();
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::script::provider::XScriptProvider >
+ createProvider( ProviderDetails& details );
+ bool isInDenyList( const OUString& serviceName ) const;
+ css::uno::Sequence< OUString > m_sDenyList;
+ ProviderDetails_hash m_hProviderDetailsCache;
+ std::mutex m_mutex;
+ css::uno::Sequence< css::uno::Any > m_Sctx;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ css::uno::Reference< css::lang::XMultiComponentFactory > m_xMgr;
+
+
+};
+} // func_provider
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/URIHelper.cxx b/scripting/source/provider/URIHelper.cxx
new file mode 100644
index 0000000000..4b122da8b1
--- /dev/null
+++ b/scripting/source/provider/URIHelper.cxx
@@ -0,0 +1,256 @@
+/* -*- 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 <config_folders.h>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include "URIHelper.hxx"
+
+namespace func_provider
+{
+
+namespace uno = ::com::sun::star::uno;
+namespace ucb = ::com::sun::star::ucb;
+namespace lang = ::com::sun::star::lang;
+namespace uri = ::com::sun::star::uri;
+
+constexpr OUString SHARE = u"share"_ustr;
+
+constexpr OUStringLiteral SHARE_UNO_PACKAGES_URI =
+ u"vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE";
+
+constexpr OUString USER = u"user"_ustr;
+constexpr OUString USER_URI =
+ u"vnd.sun.star.expand:${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap") "::UserInstallation}"_ustr;
+
+
+
+ScriptingFrameworkURIHelper::ScriptingFrameworkURIHelper(
+ const uno::Reference< uno::XComponentContext >& xContext)
+{
+ try
+ {
+ m_xSimpleFileAccess = ucb::SimpleFileAccess::create(xContext);
+ }
+ catch (uno::Exception&)
+ {
+ OSL_FAIL("Scripting Framework error initialising XSimpleFileAccess");
+ }
+
+ try
+ {
+ m_xUriReferenceFactory = uri::UriReferenceFactory::create( xContext );
+ }
+ catch (uno::Exception&)
+ {
+ OSL_FAIL("Scripting Framework error initialising XUriReferenceFactory");
+ }
+}
+
+ScriptingFrameworkURIHelper::~ScriptingFrameworkURIHelper()
+{
+ // currently does nothing
+}
+
+void SAL_CALL
+ScriptingFrameworkURIHelper::initialize(
+ const uno::Sequence < uno::Any >& args )
+{
+ if ( args.getLength() != 2 ||
+ args[0].getValueType() != ::cppu::UnoType<OUString>::get() ||
+ args[1].getValueType() != ::cppu::UnoType<OUString>::get() )
+ {
+ throw uno::RuntimeException( "ScriptingFrameworkURIHelper got invalid argument list" );
+ }
+
+ if ( !(args[0] >>= m_sLanguage) || !(args[1] >>= m_sLocation) )
+ {
+ throw uno::RuntimeException( "ScriptingFrameworkURIHelper error parsing args" );
+ }
+
+ SCRIPTS_PART = "/Scripts/" + m_sLanguage.toAsciiLowerCase();
+
+ if ( !initBaseURI() )
+ {
+ throw uno::RuntimeException( "ScriptingFrameworkURIHelper cannot find script directory" );
+ }
+}
+
+bool
+ScriptingFrameworkURIHelper::initBaseURI()
+{
+ OUString uri, test;
+ bool bAppendScriptsPart = false;
+
+ if ( m_sLocation == USER )
+ {
+ test = USER;
+ uri = USER_URI;
+ bAppendScriptsPart = true;
+ }
+ else if ( m_sLocation == "user:uno_packages" )
+ {
+ test = "uno_packages";
+ uri = USER_URI + "/user/uno_packages/cache";
+ }
+ else if (m_sLocation == SHARE)
+ {
+ test = SHARE;
+ uri = "vnd.sun.star.expand:$BRAND_BASE_DIR";
+ bAppendScriptsPart = true;
+ }
+ else if (m_sLocation == "share:uno_packages")
+ {
+ test = "uno_packages";
+ uri = SHARE_UNO_PACKAGES_URI;
+ }
+ else if (m_sLocation.startsWith("vnd.sun.star.tdoc"))
+ {
+ m_sBaseURI = m_sLocation + SCRIPTS_PART;
+ m_sLocation = "document";
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+ if ( !m_xSimpleFileAccess->exists( uri ) ||
+ !m_xSimpleFileAccess->isFolder( uri ) )
+ {
+ return false;
+ }
+
+ const uno::Sequence< OUString > children =
+ m_xSimpleFileAccess->getFolderContents( uri, true );
+
+ auto pChild = std::find_if(children.begin(), children.end(), [&test](const OUString& child) {
+ sal_Int32 idx = child.lastIndexOf(test);
+ return idx != -1 && (idx + test.getLength()) == child.getLength();
+ });
+ if (pChild != children.end())
+ {
+ if ( bAppendScriptsPart )
+ {
+ m_sBaseURI = *pChild + SCRIPTS_PART;
+ }
+ else
+ {
+ m_sBaseURI = *pChild;
+ }
+ return true;
+ }
+ return false;
+}
+
+OUString
+ScriptingFrameworkURIHelper::getLanguagePart(std::u16string_view rStorageURI)
+{
+ OUString result;
+
+ size_t idx = rStorageURI.find(m_sBaseURI);
+ sal_Int32 len = m_sBaseURI.getLength() + 1;
+
+ if ( idx != std::u16string_view::npos )
+ {
+ result = rStorageURI.substr(idx + len);
+ result = result.replace('/', '|');
+ }
+ return result;
+}
+
+OUString
+ScriptingFrameworkURIHelper::getLanguagePath(const OUString& rLanguagePart)
+{
+ OUString result = rLanguagePart.replace('|', '/');
+ return result;
+}
+
+OUString SAL_CALL
+ScriptingFrameworkURIHelper::getScriptURI(const OUString& rStorageURI)
+{
+ return
+ "vnd.sun.star.script:" +
+ getLanguagePart(rStorageURI) +
+ "?language=" +
+ m_sLanguage +
+ "&location=" +
+ m_sLocation;
+}
+
+OUString SAL_CALL
+ScriptingFrameworkURIHelper::getStorageURI(const OUString& rScriptURI)
+{
+ OUString sLanguagePart;
+ try
+ {
+ uno::Reference < uri::XVndSunStarScriptUrl > xURI(
+ m_xUriReferenceFactory->parse( rScriptURI ), uno::UNO_QUERY_THROW );
+ sLanguagePart = xURI->getName();
+ }
+ catch ( uno::Exception& )
+ {
+ throw lang::IllegalArgumentException(
+ "Script URI not valid",
+ uno::Reference< uno::XInterface >(), 1 );
+ }
+
+ return m_sBaseURI + "/" + getLanguagePath(sLanguagePart);
+}
+
+OUString SAL_CALL
+ScriptingFrameworkURIHelper::getRootStorageURI()
+{
+ return m_sBaseURI;
+}
+
+OUString SAL_CALL
+ScriptingFrameworkURIHelper::getImplementationName()
+{
+ return
+ "com.sun.star.script.provider.ScriptURIHelper";
+}
+
+sal_Bool SAL_CALL
+ScriptingFrameworkURIHelper::supportsService( const OUString& serviceName )
+{
+ return cppu::supportsService( this, serviceName );
+}
+
+uno::Sequence< OUString > SAL_CALL
+ScriptingFrameworkURIHelper::getSupportedServiceNames()
+{
+ return { "com.sun.star.script.provider.ScriptURIHelper" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_ScriptingFrameworkURIHelper_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ScriptingFrameworkURIHelper(context));
+}
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/provider/URIHelper.hxx b/scripting/source/provider/URIHelper.hxx
new file mode 100644
index 0000000000..9d200a1a37
--- /dev/null
+++ b/scripting/source/provider/URIHelper.hxx
@@ -0,0 +1,87 @@
+/* -*- 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/script/provider/XScriptURIHelper.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <com/sun/star/uri/XUriReferenceFactory.hpp>
+
+#include <rtl/ustring.hxx>
+#include <cppuhelper/implbase.hxx>
+
+namespace func_provider
+{
+
+class ScriptingFrameworkURIHelper :
+ public ::cppu::WeakImplHelper<
+ css::script::provider::XScriptURIHelper,
+ css::lang::XServiceInfo,
+ css::lang::XInitialization >
+{
+private:
+
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSimpleFileAccess;
+ css::uno::Reference<css::uri::XUriReferenceFactory> m_xUriReferenceFactory;
+
+ OUString m_sLanguage;
+ OUString m_sLocation;
+ OUString m_sBaseURI;
+
+ OUString SCRIPTS_PART;
+
+ bool initBaseURI();
+ OUString getLanguagePart(std::u16string_view rStorageURI);
+ static OUString getLanguagePath(const OUString& rLanguagePart);
+
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScriptingFrameworkURIHelper(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext );
+
+ virtual ~ScriptingFrameworkURIHelper() override;
+
+ virtual void SAL_CALL
+ initialize( const css::uno::Sequence < css::uno::Any > & args ) override;
+
+ virtual OUString SAL_CALL
+ getRootStorageURI() override;
+
+ virtual OUString SAL_CALL
+ getScriptURI( const OUString& rStorageURI ) override;
+
+ virtual OUString SAL_CALL
+ getStorageURI( const OUString& rScriptURI ) override;
+
+ 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;
+};
+
+} // namespace func_provider
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/pyprov/mailmerge.README b/scripting/source/pyprov/mailmerge.README
new file mode 100644
index 0000000000..6b4fb5ba4f
--- /dev/null
+++ b/scripting/source/pyprov/mailmerge.README
@@ -0,0 +1,18 @@
+Easiest way I find to test this is to...
+
+1)
+
+a) install fakemail and run it
+b) tools->options->writer->mail merge email
+c) localhost 8025
+
+2)
+
+a) type some text into writer that will exercise utf-8, e.g. "Caolán's test"
+b) tools->mail merge wizard->next->email message->select address book
+c) create, add one user with your own email address, ok
+d) next, next, text, send merged document as email
+e) and test all of plain text, html and the various attachment options
+
+fake mail will dump the mail it gets into its pwd, if all that works, you can
+then try with your own normal mail server.
diff --git a/scripting/source/pyprov/mailmerge.component b/scripting/source/pyprov/mailmerge.component
new file mode 100644
index 0000000000..8a93253412
--- /dev/null
+++ b/scripting/source/pyprov/mailmerge.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.Python"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.pyuno.MailMessage">
+ <service name="com.sun.star.mail.MailMessage"/>
+ </implementation>
+ <implementation name="org.openoffice.pyuno.MailServiceProvider">
+ <service name="com.sun.star.mail.MailServiceProvider"/>
+ </implementation>
+</component>
diff --git a/scripting/source/pyprov/mailmerge.py b/scripting/source/pyprov/mailmerge.py
new file mode 100644
index 0000000000..3c781c52f2
--- /dev/null
+++ b/scripting/source/pyprov/mailmerge.py
@@ -0,0 +1,533 @@
+# Caolan McNamara caolanm@redhat.com
+# a simple email mailmerge component
+
+# manual installation for hackers, not necessary for users
+# cp mailmerge.py /usr/lib/libreoffice/program
+# cd /usr/lib/libreoffice/program
+# ./unopkg add --shared mailmerge.py
+# edit ~/.openoffice.org2/user/registry/data/org/openoffice/Office/Writer.xcu
+# and change EMailSupported to as follows...
+# <prop oor:name="EMailSupported" oor:type="xs:boolean">
+# <value>true</value>
+# </prop>
+
+import unohelper
+import uno
+import re
+import os
+import encodings.idna
+
+#to implement com::sun::star::mail::XMailServiceProvider
+#and
+#to implement com.sun.star.mail.XMailMessage
+
+from com.sun.star.mail import XMailServiceProvider
+from com.sun.star.mail import XMailService
+from com.sun.star.mail import XSmtpService
+from com.sun.star.mail import XConnectionListener
+from com.sun.star.mail import XAuthenticator
+from com.sun.star.mail import XMailMessage
+from com.sun.star.mail.MailServiceType import SMTP
+from com.sun.star.mail.MailServiceType import POP3
+from com.sun.star.mail.MailServiceType import IMAP
+from com.sun.star.uno import XCurrentContext
+from com.sun.star.lang import IllegalArgumentException
+from com.sun.star.lang import EventObject
+from com.sun.star.lang import XServiceInfo
+from com.sun.star.mail import SendMailMessageFailedException
+
+from email.mime.base import MIMEBase
+from email.message import Message
+from email.charset import Charset
+from email.charset import QP
+from email.encoders import encode_base64
+from email.header import Header
+from email.mime.multipart import MIMEMultipart
+from email.utils import formatdate
+from email.utils import parseaddr
+from socket import _GLOBAL_DEFAULT_TIMEOUT
+
+import sys, ssl, smtplib, imaplib, poplib
+dbg = False
+
+# pythonloader looks for a static g_ImplementationHelper variable
+g_ImplementationHelper = unohelper.ImplementationHelper()
+g_providerImplName = "org.openoffice.pyuno.MailServiceProvider"
+g_messageImplName = "org.openoffice.pyuno.MailMessage"
+
+def prepareTLSContext(xComponent, xContext, isTLSRequested):
+ xConfigProvider = xContext.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider")
+ prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
+ prop.Name = "nodepath"
+ prop.Value = "/org.openoffice.Office.Security/Net"
+ xSettings = xConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
+ (prop,))
+ isAllowedInsecure = xSettings.getByName("AllowInsecureProtocols")
+ tlscontext = None
+ if isTLSRequested:
+ if dbg:
+ print("SSL config: " + str(ssl.get_default_verify_paths()), file=sys.stderr)
+ tlscontext = ssl.create_default_context()
+ # SSLv2/v3 is already disabled by default.
+ # This check does not work, because OpenSSL 3 defines SSL_OP_NO_SSLv2
+ # as 0, so even though _ssl__SSLContext_impl() tries to set it,
+ # getting the value from SSL_CTX_get_options() doesn't lead to setting
+ # the python-level flag.
+ #assert (tlscontext.options & ssl.Options.OP_NO_SSLv2) != 0
+ assert (tlscontext.options & ssl.Options.OP_NO_SSLv3) != 0
+ if not(isAllowedInsecure):
+ if not(isTLSRequested):
+ if dbg:
+ print("mailmerge.py: insecure connection not allowed by configuration", file=sys.stderr)
+ raise IllegalArgumentException("insecure connection not allowed by configuration", xComponent, 1)
+ tlscontext.options |= ssl.Options.OP_NO_TLSv1 | ssl.Options.OP_NO_TLSv1_1
+ return tlscontext
+
+class PyMailSMTPService(unohelper.Base, XSmtpService):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+ self.listeners = []
+ self.supportedtypes = ('Insecure', 'Ssl')
+ self.server = None
+ self.connectioncontext = None
+ self.notify = EventObject(self)
+ if dbg:
+ print("PyMailSMTPService init", file=sys.stderr)
+ print("python version is: " + sys.version, file=sys.stderr)
+ def addConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailSMTPService addConnectionListener", file=sys.stderr)
+ self.listeners.append(xListener)
+ def removeConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailSMTPService removeConnectionListener", file=sys.stderr)
+ self.listeners.remove(xListener)
+ def getSupportedConnectionTypes(self):
+ if dbg:
+ print("PyMailSMTPService getSupportedConnectionTypes", file=sys.stderr)
+ return self.supportedtypes
+ def connect(self, xConnectionContext, xAuthenticator):
+ self.connectioncontext = xConnectionContext
+ if dbg:
+ print("PyMailSMTPService connect", file=sys.stderr)
+ server = xConnectionContext.getValueByName("ServerName").strip()
+ if dbg:
+ print("ServerName: " + server, file=sys.stderr)
+ port = int(xConnectionContext.getValueByName("Port"))
+ if dbg:
+ print("Port: " + str(port), file=sys.stderr)
+ tout = xConnectionContext.getValueByName("Timeout")
+ if dbg:
+ print(isinstance(tout,int), file=sys.stderr)
+ if not isinstance(tout,int):
+ tout = _GLOBAL_DEFAULT_TIMEOUT
+ if dbg:
+ print("Timeout: " + str(tout), file=sys.stderr)
+ connectiontype = xConnectionContext.getValueByName("ConnectionType")
+ if dbg:
+ print("ConnectionType: " + connectiontype, file=sys.stderr)
+ tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == 'SSL' or port == 465)
+ if port == 465:
+ self.server = smtplib.SMTP_SSL(server, port, timeout=tout, context=tlscontext)
+ else:
+ self.server = smtplib.SMTP(server, port,timeout=tout)
+
+ if dbg:
+ self.server.set_debuglevel(1)
+
+ if connectiontype.upper() == 'SSL' and port != 465:
+ # STRIPTLS: smtplib raises an exception if result is not 220
+ self.server.starttls(context=tlscontext)
+
+ user = xAuthenticator.getUserName()
+ password = xAuthenticator.getPassword()
+ if user != '':
+ if dbg:
+ print("Logging in, username of: " + user, file=sys.stderr)
+ self.server.login(user, password)
+
+ for listener in self.listeners:
+ listener.connected(self.notify)
+ def disconnect(self):
+ if dbg:
+ print("PyMailSMTPService disconnect", file=sys.stderr)
+ if self.server:
+ self.server.quit()
+ self.server = None
+ for listener in self.listeners:
+ listener.disconnected(self.notify)
+ def isConnected(self):
+ if dbg:
+ print("PyMailSMTPService isConnected", file=sys.stderr)
+ return self.server != None
+ def getCurrentConnectionContext(self):
+ if dbg:
+ print("PyMailSMTPService getCurrentConnectionContext", file=sys.stderr)
+ return self.connectioncontext
+ def sendMailMessage(self, xMailMessage):
+ COMMASPACE = ', '
+
+ if dbg:
+ print("PyMailSMTPService sendMailMessage", file=sys.stderr)
+ recipients = xMailMessage.getRecipients()
+ sendermail = xMailMessage.SenderAddress
+ sendername = xMailMessage.SenderName
+ subject = xMailMessage.Subject
+ ccrecipients = xMailMessage.getCcRecipients()
+ bccrecipients = xMailMessage.getBccRecipients()
+ if dbg:
+ print("PyMailSMTPService subject: " + subject, file=sys.stderr)
+ print("PyMailSMTPService from: " + sendername, file=sys.stderr)
+ print("PyMailSMTPService from: " + sendermail, file=sys.stderr)
+ print("PyMailSMTPService send to: %s" % (recipients,), file=sys.stderr)
+
+ attachments = xMailMessage.getAttachments()
+
+ textmsg = Message()
+
+ content = xMailMessage.Body
+ flavors = content.getTransferDataFlavors()
+ if dbg:
+ print("PyMailSMTPService flavors len: %d" % (len(flavors),), file=sys.stderr)
+
+ #Use first flavor that's sane for an email body
+ for flavor in flavors:
+ if flavor.MimeType.find('text/html') != -1 or flavor.MimeType.find('text/plain') != -1:
+ if dbg:
+ print("PyMailSMTPService mimetype is: " + flavor.MimeType, file=sys.stderr)
+ textbody = content.getTransferData(flavor)
+
+ if len(textbody):
+ mimeEncoding = re.sub("charset=.*", "charset=UTF-8", flavor.MimeType)
+ if mimeEncoding.find('charset=UTF-8') == -1:
+ mimeEncoding = mimeEncoding + "; charset=UTF-8"
+ textmsg['Content-Type'] = mimeEncoding
+ textmsg['MIME-Version'] = '1.0'
+
+ try:
+ #it's a string, get it as utf-8 bytes
+ textbody = textbody.encode('utf-8')
+ except:
+ #it's a bytesequence, get raw bytes
+ textbody = textbody.value
+ textbody = textbody.decode('utf-8')
+ c = Charset('utf-8')
+ c.body_encoding = QP
+ textmsg.set_payload(textbody, c)
+
+ break
+
+ if (len(attachments)):
+ msg = MIMEMultipart()
+ msg.epilogue = ''
+ msg.attach(textmsg)
+ else:
+ msg = textmsg
+
+ hdr = Header(sendername, 'utf-8')
+ hdr.append('<'+sendermail+'>','us-ascii')
+ msg['Subject'] = subject
+ msg['From'] = hdr
+ msg['To'] = COMMASPACE.join(recipients)
+ if len(ccrecipients):
+ msg['Cc'] = COMMASPACE.join(ccrecipients)
+ if xMailMessage.ReplyToAddress != '':
+ msg['Reply-To'] = xMailMessage.ReplyToAddress
+
+ mailerstring = "LibreOffice via Caolan's mailmerge component"
+ try:
+ ctx = uno.getComponentContext()
+ aConfigProvider = ctx.ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider")
+ prop = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
+ prop.Name = "nodepath"
+ prop.Value = "/org.openoffice.Setup/Product"
+ aSettings = aConfigProvider.createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",
+ (prop,))
+ mailerstring = aSettings.getByName("ooName") + " " + \
+ aSettings.getByName("ooSetupVersion") + " via Caolan's mailmerge component"
+ except:
+ pass
+
+ msg['X-Mailer'] = mailerstring
+ msg['Date'] = formatdate(localtime=True)
+
+ for attachment in attachments:
+ content = attachment.Data
+ flavors = content.getTransferDataFlavors()
+ flavor = flavors[0]
+ ctype = flavor.MimeType
+ maintype, subtype = ctype.split('/', 1)
+ msgattachment = MIMEBase(maintype, subtype)
+ data = content.getTransferData(flavor)
+ msgattachment.set_payload(data.value)
+ encode_base64(msgattachment)
+ fname = attachment.ReadableName
+ try:
+ msgattachment.add_header('Content-Disposition', 'attachment', \
+ filename=fname)
+ except:
+ msgattachment.add_header('Content-Disposition', 'attachment', \
+ filename=('utf-8','',fname))
+ if dbg:
+ print(("PyMailSMTPService attachmentheader: ", str(msgattachment)), file=sys.stderr)
+
+ msg.attach(msgattachment)
+
+ uniquer = {}
+ for key in recipients:
+ uniquer[key] = True
+ if len(ccrecipients):
+ for key in ccrecipients:
+ uniquer[key] = True
+ if len(bccrecipients):
+ for key in bccrecipients:
+ uniquer[key] = True
+ truerecipients = uniquer.keys()
+
+ if dbg:
+ print(("PyMailSMTPService recipients are: ", truerecipients), file=sys.stderr)
+
+ self.server.sendmail(sendermail, truerecipients, msg.as_string())
+
+class PyMailIMAPService(unohelper.Base, XMailService):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+ self.listeners = []
+ self.supportedtypes = ('Insecure', 'Ssl')
+ self.server = None
+ self.connectioncontext = None
+ self.notify = EventObject(self)
+ if dbg:
+ print("PyMailIMAPService init", file=sys.stderr)
+ def addConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailIMAPService addConnectionListener", file=sys.stderr)
+ self.listeners.append(xListener)
+ def removeConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailIMAPService removeConnectionListener", file=sys.stderr)
+ self.listeners.remove(xListener)
+ def getSupportedConnectionTypes(self):
+ if dbg:
+ print("PyMailIMAPService getSupportedConnectionTypes", file=sys.stderr)
+ return self.supportedtypes
+ def connect(self, xConnectionContext, xAuthenticator):
+ if dbg:
+ print("PyMailIMAPService connect", file=sys.stderr)
+
+ self.connectioncontext = xConnectionContext
+ server = xConnectionContext.getValueByName("ServerName")
+ if dbg:
+ print(server, file=sys.stderr)
+ port = int(xConnectionContext.getValueByName("Port"))
+ if dbg:
+ print(port, file=sys.stderr)
+ connectiontype = xConnectionContext.getValueByName("ConnectionType")
+ if dbg:
+ print(connectiontype, file=sys.stderr)
+ tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == 'SSL')
+ print("BEFORE", file=sys.stderr)
+ if connectiontype.upper() == 'SSL':
+ self.server = imaplib.IMAP4_SSL(server, port, ssl_context=tlscontext)
+ else:
+ self.server = imaplib.IMAP4(server, port)
+ print("AFTER", file=sys.stderr)
+
+ user = xAuthenticator.getUserName()
+ password = xAuthenticator.getPassword()
+ if user != '':
+ if dbg:
+ print("Logging in, username of: " + user, file=sys.stderr)
+ self.server.login(user, password)
+
+ for listener in self.listeners:
+ listener.connected(self.notify)
+ def disconnect(self):
+ if dbg:
+ print("PyMailIMAPService disconnect", file=sys.stderr)
+ if self.server:
+ self.server.logout()
+ self.server = None
+ for listener in self.listeners:
+ listener.disconnected(self.notify)
+ def isConnected(self):
+ if dbg:
+ print("PyMailIMAPService isConnected", file=sys.stderr)
+ return self.server != None
+ def getCurrentConnectionContext(self):
+ if dbg:
+ print("PyMailIMAPService getCurrentConnectionContext", file=sys.stderr)
+ return self.connectioncontext
+
+class PyMailPOP3Service(unohelper.Base, XMailService):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+ self.listeners = []
+ self.supportedtypes = ('Insecure', 'Ssl')
+ self.server = None
+ self.connectioncontext = None
+ self.notify = EventObject(self)
+ if dbg:
+ print("PyMailPOP3Service init", file=sys.stderr)
+ def addConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailPOP3Service addConnectionListener", file=sys.stderr)
+ self.listeners.append(xListener)
+ def removeConnectionListener(self, xListener):
+ if dbg:
+ print("PyMailPOP3Service removeConnectionListener", file=sys.stderr)
+ self.listeners.remove(xListener)
+ def getSupportedConnectionTypes(self):
+ if dbg:
+ print("PyMailPOP3Service getSupportedConnectionTypes", file=sys.stderr)
+ return self.supportedtypes
+ def connect(self, xConnectionContext, xAuthenticator):
+ if dbg:
+ print("PyMailPOP3Service connect", file=sys.stderr)
+
+ self.connectioncontext = xConnectionContext
+ server = xConnectionContext.getValueByName("ServerName")
+ if dbg:
+ print(server, file=sys.stderr)
+ port = int(xConnectionContext.getValueByName("Port"))
+ if dbg:
+ print(port, file=sys.stderr)
+ connectiontype = xConnectionContext.getValueByName("ConnectionType")
+ if dbg:
+ print(connectiontype, file=sys.stderr)
+ tlscontext = prepareTLSContext(self, self.ctx, connectiontype.upper() == 'SSL')
+ print("BEFORE", file=sys.stderr)
+ if connectiontype.upper() == 'SSL':
+ self.server = poplib.POP3_SSL(server, port, context=tlscontext)
+ else:
+ tout = xConnectionContext.getValueByName("Timeout")
+ if dbg:
+ print(isinstance(tout,int), file=sys.stderr)
+ if not isinstance(tout,int):
+ tout = _GLOBAL_DEFAULT_TIMEOUT
+ if dbg:
+ print("Timeout: " + str(tout), file=sys.stderr)
+ self.server = poplib.POP3(server, port, timeout=tout)
+ print("AFTER", file=sys.stderr)
+
+ user = xAuthenticator.getUserName()
+ password = xAuthenticator.getPassword()
+ if dbg:
+ print("Logging in, username of: " + user, file=sys.stderr)
+ self.server.user(user)
+ self.server.pass_(password)
+
+ for listener in self.listeners:
+ listener.connected(self.notify)
+ def disconnect(self):
+ if dbg:
+ print("PyMailPOP3Service disconnect", file=sys.stderr)
+ if self.server:
+ self.server.quit()
+ self.server = None
+ for listener in self.listeners:
+ listener.disconnected(self.notify)
+ def isConnected(self):
+ if dbg:
+ print("PyMailPOP3Service isConnected", file=sys.stderr)
+ return self.server != None
+ def getCurrentConnectionContext(self):
+ if dbg:
+ print("PyMailPOP3Service getCurrentConnectionContext", file=sys.stderr)
+ return self.connectioncontext
+
+class PyMailServiceProvider(unohelper.Base, XMailServiceProvider, XServiceInfo):
+ def __init__( self, ctx ):
+ if dbg:
+ print("PyMailServiceProvider init", file=sys.stderr)
+ self.ctx = ctx
+ def create(self, aType):
+ if dbg:
+ print("PyMailServiceProvider create with", aType, file=sys.stderr)
+ if aType == SMTP:
+ return PyMailSMTPService(self.ctx);
+ elif aType == POP3:
+ return PyMailPOP3Service(self.ctx);
+ elif aType == IMAP:
+ return PyMailIMAPService(self.ctx);
+ else:
+ print("PyMailServiceProvider, unknown TYPE " + aType, file=sys.stderr)
+
+ def getImplementationName(self):
+ return g_providerImplName
+
+ def supportsService(self, ServiceName):
+ return g_ImplementationHelper.supportsService(g_providerImplName, ServiceName)
+
+ def getSupportedServiceNames(self):
+ return g_ImplementationHelper.getSupportedServiceNames(g_providerImplName)
+
+class PyMailMessage(unohelper.Base, XMailMessage):
+ def __init__( self, ctx, sTo='', sFrom='', Subject='', Body=None, aMailAttachment=None ):
+ if dbg:
+ print("PyMailMessage init", file=sys.stderr)
+ self.ctx = ctx
+
+ self.recipients = [sTo]
+ self.ccrecipients = []
+ self.bccrecipients = []
+ self.aMailAttachments = []
+ if aMailAttachment != None:
+ self.aMailAttachments.append(aMailAttachment)
+
+ self.SenderName, self.SenderAddress = parseaddr(sFrom)
+ self.ReplyToAddress = sFrom
+ self.Subject = Subject
+ self.Body = Body
+ if dbg:
+ print("post PyMailMessage init", file=sys.stderr)
+ def addRecipient( self, recipient ):
+ if dbg:
+ print("PyMailMessage.addRecipient: " + recipient, file=sys.stderr)
+ self.recipients.append(recipient)
+ def addCcRecipient( self, ccrecipient ):
+ if dbg:
+ print("PyMailMessage.addCcRecipient: " + ccrecipient, file=sys.stderr)
+ self.ccrecipients.append(ccrecipient)
+ def addBccRecipient( self, bccrecipient ):
+ if dbg:
+ print("PyMailMessage.addBccRecipient: " + bccrecipient, file=sys.stderr)
+ self.bccrecipients.append(bccrecipient)
+ def getRecipients( self ):
+ if dbg:
+ print("PyMailMessage.getRecipients: " + str(self.recipients), file=sys.stderr)
+ return tuple(self.recipients)
+ def getCcRecipients( self ):
+ if dbg:
+ print("PyMailMessage.getCcRecipients: " + str(self.ccrecipients), file=sys.stderr)
+ return tuple(self.ccrecipients)
+ def getBccRecipients( self ):
+ if dbg:
+ print("PyMailMessage.getBccRecipients: " + str(self.bccrecipients), file=sys.stderr)
+ return tuple(self.bccrecipients)
+ def addAttachment( self, aMailAttachment ):
+ if dbg:
+ print("PyMailMessage.addAttachment", file=sys.stderr)
+ self.aMailAttachments.append(aMailAttachment)
+ def getAttachments( self ):
+ if dbg:
+ print("PyMailMessage.getAttachments", file=sys.stderr)
+ return tuple(self.aMailAttachments)
+
+ def getImplementationName(self):
+ return g_messageImplName
+
+ def supportsService(self, ServiceName):
+ return g_ImplementationHelper.supportsService(g_messageImplName, ServiceName)
+
+ def getSupportedServiceNames(self):
+ return g_ImplementationHelper.getSupportedServiceNames(g_messageImplName)
+
+g_ImplementationHelper.addImplementation( \
+ PyMailServiceProvider, g_providerImplName,
+ ("com.sun.star.mail.MailServiceProvider",),)
+g_ImplementationHelper.addImplementation( \
+ PyMailMessage, g_messageImplName,
+ ("com.sun.star.mail.MailMessage",),)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/scripting/source/pyprov/msgbox.py b/scripting/source/pyprov/msgbox.py
new file mode 100644
index 0000000000..f9c93df174
--- /dev/null
+++ b/scripting/source/pyprov/msgbox.py
@@ -0,0 +1,241 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/.
+#
+
+# prepare Python environment - Add the path of this class
+from os import path
+from sys import modules
+from sys import path as syspath
+
+# pyUNO program itself
+import uno, unohelper
+
+# UNO GUI toolkit
+from com.sun.star.awt.WindowClass import TOP, SIMPLE
+from com.sun.star.awt.PushButtonType import STANDARD as standard
+from com.sun.star.awt.PushButtonType import OK as ok
+from com.sun.star.awt.PushButtonType import CANCEL as cancel
+from com.sun.star.awt.PushButtonType import HELP as help
+from com.sun.star.awt.TextAlign import CENTER as center
+from com.sun.star.awt.TextAlign import LEFT as left
+from com.sun.star.awt.TextAlign import RIGHT as right
+
+# used UNO listeners
+from com.sun.star.awt import XActionListener
+
+class MsgBox(unohelper.Base):
+ """Inspect UNO object, link to sdk and recursive calls"""
+
+ def __init__(self, aContext):
+ """acontext : a Valid UNO context
+ """
+
+ self.VERSION = '0.1'
+ self.ctx = aContext
+ self.smgr = aContext.ServiceManager
+ # UI Dialog object
+ self.dialog=None
+ # List of opened Listeners
+ self.lst_listeners={}
+ #UI parameters
+ self.ButtonSize = 50
+ self.boxSize = 200
+ self.lineHeight = 10
+ self.fromBroxSize = False
+ self.numberOfLines = -1
+
+ self.Buttons = []
+ self.Response = ''
+
+ return
+
+ #####################################################
+ # GUI definition #
+ #####################################################
+ def _createBox(self):
+ """Create the Box"""
+
+ # computes parameters of the message dialog
+ if self.numberOfLines == -1:
+ #calculate
+ numberOfLines = len(self.message.split(chr(10)))
+ else:
+ numberOfLines = self.numberOfLines
+
+ numberOfButtons = len(self.Buttons)
+ self.ButtonSpace = self.ButtonSize/2
+ if self.fromBroxSize:
+ # button size is calculated from boxsize
+ size = (2 * self.boxSize) / (3 * numberOfButtons + 1)
+ self.ButtonSize = size
+ self.ButtonSpace = self.ButtonSize/2
+ else:
+ # boxsize is calculated from buttonsize
+ self.boxSize = numberOfButtons * (self.ButtonSize +
+ self.ButtonSpace) + self.ButtonSpace
+
+ # create the dialog model and set the properties
+ dialog_model = self.smgr.createInstanceWithContext(
+ 'com.sun.star.awt.UnoControlDialogModel',
+ self.ctx)
+ dialog_model.PositionX = 50
+ dialog_model.Step = 1
+ dialog_model.TabIndex = 7
+ dialog_model.Width = self.boxSize#numberOfButtons * (self.ButtonSize +
+ # self.ButtonSpace) + 25
+ dialog_model.Height = 10 + self.lineHeight * numberOfLines + 10 + 12 + 10
+ dialog_model.PositionY = 63
+ dialog_model.Sizeable = True
+ dialog_model.Closeable = False
+
+ dialog = self.smgr.createInstanceWithContext(
+ 'com.sun.star.awt.UnoControlDialog', self.ctx)
+
+ # label Label0
+ label = dialog_model.createInstance(
+ 'com.sun.star.awt.UnoControlFixedTextModel')
+ label.PositionX = 10
+ label.TabIndex = 9
+ label.Width = dialog_model.Width - label.PositionX
+ label.Height = self.lineHeight* numberOfLines
+ label.PositionY = 10
+ label.Align = left
+ label.MultiLine = True
+ label.Label = self.message
+ dialog_model.insertByName('Label0', label)
+
+ nb = 0
+ for buttonName in self.Buttons:
+ nb +=1
+ button = dialog_model.createInstance(
+ 'com.sun.star.awt.UnoControlButtonModel')
+ button.PositionX = nb * self.ButtonSpace + (nb-1)* self.ButtonSize
+ button.TabIndex = 8
+ button.Height = 12
+ button.Width = self.ButtonSize
+ button.PositionY = 10 + label.Height + 10
+ button.PushButtonType = standard
+ if nb == 1:
+ button.DefaultButton = True
+ else:
+ button.DefaultButton = False
+ button.Label = buttonName
+ dialog_model.insertByName('Btn' + str(nb), button )
+
+ if not dialog.getModel():
+ dialog.setModel(dialog_model)
+
+ # UNO toolkit definition
+ toolkit = self.smgr.createInstanceWithContext('com.sun.star.awt.Toolkit', self.ctx)
+ a_rect = uno.createUnoStruct( 'com.sun.star.awt.Rectangle' )
+ a_rect.X = 50
+ dialog.setTitle ( self.title )
+ a_rect.Width = 270
+ a_rect.Height = 261
+ a_rect.Y = 63
+ win_descriptor = uno.createUnoStruct('com.sun.star.awt.WindowDescriptor')
+ win_descriptor.Type = TOP
+ win_descriptor.ParentIndex = -1
+ win_descriptor.Bounds = a_rect
+ peer = toolkit.createWindow( win_descriptor )
+ dialog.createPeer( toolkit, peer )
+
+ return dialog
+
+ def _addListeners(self):
+ """Add listeners to dialog"""
+ nb = 0
+ for buttonName in self.Buttons:
+ nb +=1
+ a_control = self.dialog.getControl('Btn'+str(nb))
+ the_listener = ButtonListener(self)
+ a_control.addActionListener(the_listener)
+ self.lst_listeners['Btn'+str(nb)] = the_listener
+ return
+
+ def _removeListeners(self):
+ """ remove listeners on exiting"""
+ nb = 0
+ for buttonName in self.Buttons:
+ nb +=1
+ a_control = self.dialog.getControl('Btn'+str(nb))
+ a_control.removeActionListener(self.lst_listeners['Btn'+str(nb)])
+ return
+
+ def show(self, message, decoration, title):
+ self.message = message
+ self.decoration = decoration
+ self.title = title
+ # Create GUI
+ self.dialog = self._createBox()
+ self._addListeners()
+ #execute the dialog --> blocking call
+ self.dialog.execute()
+ #end --> release listeners and dispose dialog
+ self._removeListeners()
+ self.dialog.dispose()
+ return self.Response
+
+ def addButton(self, caption):
+ self.Buttons.append(caption)
+ return
+
+ def renderFromBoxSize(self, size = 150):
+ self.boxSize = size
+ self.fromBroxSize = True
+ return
+
+ def renderFromButtonSize(self, size = 50):
+ self.ButtonSize = size
+ self.fromBroxSize = False
+ return
+
+class ButtonListener(unohelper.Base, XActionListener):
+ """Stops the MessageBox, sets the button label as returned value"""
+ def __init__(self, caller):
+ self.caller = caller
+
+ def disposing(self, eventObject):
+ pass
+
+ def actionPerformed(self, actionEvent):
+ button = actionEvent.Source
+ self.caller.Response = button.Model.Label
+ self.caller.dialog.endExecute()
+ return
+
+### TEST
+if __name__ == '__main__':
+ # get the uno component context from the PyUNO runtime
+ localContext = uno.getComponentContext()
+
+ # create the UnoUrlResolver
+ resolver = localContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", localContext )
+
+ # connect to the running office
+ # LibO has to be launched in listen mode as
+ # ./soffice "--accept=socket,host=localhost,port=2002;urp;"
+ ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
+ myBox = MsgBox(ctx)
+ myBox.addButton("Yes")
+ myBox.addButton("No")
+ myBox.addButton("May be")
+ myBox.renderFromBoxSize(150)
+ myBox.numberOflines = 2
+
+ print(myBox.show("A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message A very long message " + chr(10)+chr(10)+"Do you agree ?",0,"Dialog title"))
+
+ myBox = MsgBox(ctx)
+ myBox.addButton("oK")
+ myBox.renderFromButtonSize()
+ myBox.numberOflines = 2
+
+ print(myBox.show("A small message",0,"Dialog title"))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/scripting/source/pyprov/pythonscript.py b/scripting/source/pyprov/pythonscript.py
new file mode 100644
index 0000000000..00d96d9b0d
--- /dev/null
+++ b/scripting/source/pyprov/pythonscript.py
@@ -0,0 +1,1146 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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 .
+#
+# XScript implementation for python
+import uno
+import unohelper
+import sys
+import os
+import types
+import time
+import ast
+import platform
+from com.sun.star.uri.RelativeUriExcessParentSegments import RETAIN
+from urllib.parse import unquote
+
+class LogLevel:
+ NONE = 0 # production level
+ ERROR = 1 # for script developers
+ DEBUG = 2 # for script framework developers
+
+PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL"
+PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT"
+
+# Configuration ----------------------------------------------------
+LogLevel.use = LogLevel.NONE
+if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR":
+ LogLevel.use = LogLevel.ERROR
+elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG":
+ LogLevel.use = LogLevel.DEBUG
+
+# True, writes to stdout (difficult on windows)
+# False, writes to user/Scripts/python/log.txt
+LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0"
+
+ENABLE_EDIT_DIALOG=False # offers a minimal editor for editing.
+#-------------------------------------------------------------------
+
+def encfile(uni):
+ return uni.encode( sys.getfilesystemencoding())
+
+def lastException2String():
+ (excType,excInstance,excTraceback) = sys.exc_info()
+ ret = str(excType) + ": "+str(excInstance) + "\n" + \
+ uno._uno_extract_printable_stacktrace( excTraceback )
+ return ret
+
+def logLevel2String( level ):
+ ret = " NONE"
+ if level == LogLevel.ERROR:
+ ret = "ERROR"
+ elif level >= LogLevel.DEBUG:
+ ret = "DEBUG"
+ return ret
+
+def getLogTarget():
+ ret = sys.stdout
+ if not LOG_STDOUT:
+ try:
+ pathSubst = uno.getComponentContext().ServiceManager.createInstance(
+ "com.sun.star.util.PathSubstitution" )
+ userInstallation = pathSubst.getSubstituteVariableValue( "user" )
+ if len( userInstallation ) > 0:
+ systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" )
+ ret = open( systemPath , "a" )
+ except:
+ print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delegating log to stdout\n")
+ return ret
+
+class Logger(LogLevel):
+ def __init__(self , target ):
+ self.target = target
+
+ def isDebugLevel( self ):
+ return self.use >= self.DEBUG
+
+ def debug( self, msg ):
+ if self.isDebugLevel():
+ self.log( self.DEBUG, msg )
+
+ def isErrorLevel( self ):
+ return self.use >= self.ERROR
+
+ def error( self, msg ):
+ if self.isErrorLevel():
+ self.log( self.ERROR, msg )
+
+ def log( self, level, msg ):
+ if self.use >= level:
+ try:
+ self.target.write(
+ time.asctime() +
+ " [" +
+ logLevel2String( level ) +
+ "] " +
+ msg +
+ "\n" )
+ self.target.flush()
+ except:
+ print("Error during writing to stdout: " +lastException2String() + "\n")
+
+log = Logger( getLogTarget() )
+
+log.debug( "pythonscript loading" )
+
+#from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
+from com.sun.star.uno import RuntimeException
+from com.sun.star.lang import IllegalArgumentException
+from com.sun.star.container import NoSuchElementException
+from com.sun.star.lang import XServiceInfo
+from com.sun.star.io import IOException
+from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command
+from com.sun.star.task import XInteractionHandler
+from com.sun.star.beans import XPropertySet, Property
+from com.sun.star.container import XNameContainer
+from com.sun.star.xml.sax import XDocumentHandler, InputSource
+from com.sun.star.uno import Exception as UnoException
+from com.sun.star.script import XInvocation
+from com.sun.star.awt import XActionListener
+
+from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException
+from com.sun.star.script.browse import XBrowseNode
+from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT
+from com.sun.star.util import XModifyListener
+
+LANGUAGENAME = "Python"
+GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
+CALLABLE_CONTAINER_NAME = "g_exportedScripts"
+
+# pythonloader looks for a static g_ImplementationHelper variable
+g_ImplementationHelper = unohelper.ImplementationHelper()
+g_implName = "org.libreoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
+
+
+
+BLOCK_SIZE = 65536
+def readTextFromStream( inputStream ):
+ # read the file
+ code = uno.ByteSequence( b"" )
+ while True:
+ read,out = inputStream.readBytes( None , BLOCK_SIZE )
+ code = code + out
+ if read < BLOCK_SIZE:
+ break
+ return code.value
+
+def toIniName( str ):
+ if platform.system() == "Windows":
+ return str + ".ini"
+ else:
+ return str + "rc"
+
+
+""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
+ scriptURI is the system independent uri
+"""
+class MyUriHelper:
+
+ def __init__( self, ctx, location ):
+ self.ctx = ctx
+ self.s_UriMap = \
+ { "share" : "vnd.sun.star.expand:$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/Scripts/python" , \
+ "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
+ "user" : "vnd.sun.star.expand:${$BRAND_INI_DIR/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
+ "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
+ self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx)
+ if location.startswith( "vnd.sun.star.tdoc" ):
+ self.m_baseUri = location + "/Scripts/python"
+ self.m_scriptUriLocation = "document"
+ else:
+ self.m_baseUri = expandUri( self.s_UriMap[location] )
+ self.m_scriptUriLocation = location
+ log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation )
+
+ def getRootStorageURI( self ):
+ return self.m_baseUri
+
+ def getStorageURI( self, scriptURI ):
+ return self.scriptURI2StorageUri(scriptURI)
+
+ def getScriptURI( self, storageURI ):
+ return self.storageURI2ScriptUri(storageURI)
+
+ def storageURI2ScriptUri( self, storageURI ):
+ if not storageURI.startswith( self.m_baseUri ):
+ message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ ret = "vnd.sun.star.script:" + \
+ storageURI[len(self.m_baseUri)+1:].replace("/","|") + \
+ "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation
+ log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret )
+ return ret
+
+ def scriptURI2StorageUri( self, scriptURI ):
+ try:
+ # base path to the python script location
+ sBaseUri = self.m_baseUri + "/"
+ xBaseUri = self.m_uriRefFac.parse(sBaseUri)
+
+ # path to the .py file + "$functionname, arguments, etc
+ xStorageUri = self.m_uriRefFac.parse(scriptURI)
+ # getName will apply url-decoding to the name, so encode back
+ sStorageUri = xStorageUri.getName().replace("%", "%25")
+ sStorageUri = sStorageUri.replace( "|", "/" )
+
+ # path to the .py file, relative to the base
+ funcNameStart = sStorageUri.find("$")
+ if funcNameStart != -1:
+ sFileUri = sStorageUri[0:funcNameStart]
+ sFuncName = sStorageUri[funcNameStart+1:]
+ else:
+ sFileUri = sStorageUri
+
+ xFileUri = self.m_uriRefFac.parse(sFileUri)
+ if not xFileUri:
+ message = "pythonscript: invalid relative uri '" + sFileUri+ "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ if not xFileUri.hasRelativePath():
+ message = "pythonscript: an absolute uri is invalid '" + sFileUri+ "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ # absolute path to the .py file
+ xAbsScriptUri = self.m_uriRefFac.makeAbsolute(xBaseUri, xFileUri, True, RETAIN)
+ sAbsScriptUri = xAbsScriptUri.getUriReference()
+
+ # ensure py file is under the base path
+ if not sAbsScriptUri.startswith(sBaseUri):
+ message = "pythonscript: storage uri '" + sAbsScriptUri + "' not in base uri '" + self.m_baseUri + "'"
+ log.debug( message )
+ raise RuntimeException( message, self.ctx )
+
+ ret = sAbsScriptUri
+ if funcNameStart != -1:
+ ret = ret + "$" + sFuncName
+ log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret )
+ return ret
+ except UnoException as e:
+ log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message)
+ raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + e.Message, self.ctx )
+ except Exception as e:
+ log.error( "error during converting scriptURI="+scriptURI + ": " + str(e))
+ raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), self.ctx )
+
+
+class ModuleEntry:
+ def __init__( self, lastRead, module ):
+ self.lastRead = lastRead
+ self.module = module
+
+def hasChanged( oldDate, newDate ):
+ return newDate.Year > oldDate.Year or \
+ newDate.Month > oldDate.Month or \
+ newDate.Day > oldDate.Day or \
+ newDate.Hours > oldDate.Hours or \
+ newDate.Minutes > oldDate.Minutes or \
+ newDate.Seconds > oldDate.Seconds or \
+ newDate.NanoSeconds > oldDate.NanoSeconds
+
+def ensureSourceState( code ):
+ if code.endswith(b"\n"):
+ code = code + b"\n"
+ code = code.replace(b"\r", b"")
+ return code
+
+
+def checkForPythonPathBesideScript( url ):
+ if url.startswith( "file:" ):
+ path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
+ log.log( LogLevel.DEBUG, "checking for existence of " + path )
+ if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
+ log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
+ sys.path.append( path )
+
+ path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
+ log.log( LogLevel.DEBUG, "checking for existence of " + path )
+ if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
+ log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
+ sys.path.append( path )
+
+
+class ScriptContext(unohelper.Base):
+ def __init__( self, ctx, doc, inv ):
+ self.ctx = ctx
+ self.doc = doc
+ self.inv = inv
+
+ # XScriptContext
+ def getDocument(self):
+ if self.doc:
+ return self.doc
+ return self.getDesktop().getCurrentComponent()
+
+ def getDesktop(self):
+ return self.ctx.ServiceManager.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", self.ctx )
+
+ def getComponentContext(self):
+ return self.ctx
+
+ def getInvocationContext(self):
+ return self.inv
+
+#----------------------------------
+# Global Module Administration
+# does not fit together with script
+# engine lifetime management
+#----------------------------------
+#g_scriptContext = ScriptContext( uno.getComponentContext(), None )
+#g_modules = {}
+#def getModuleByUrl( url, sfa ):
+# entry = g_modules.get(url)
+# load = True
+# lastRead = sfa.getDateTimeModified( url )
+# if entry:
+# if hasChanged( entry.lastRead, lastRead ):
+# log.debug("file " + url + " has changed, reloading")
+# else:
+# load = False
+#
+# if load:
+# log.debug( "opening >" + url + "<" )
+#
+# code = readTextFromStream( sfa.openFileRead( url ) )
+
+ # execute the module
+# entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
+# entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
+# entry.module.__file__ = url
+# exec code in entry.module.__dict__
+# g_modules[ url ] = entry
+# log.debug( "mapped " + url + " to " + str( entry.module ) )
+# return entry.module
+
+class ProviderContext:
+ def __init__( self, storageType, sfa, uriHelper, scriptContext ):
+ self.storageType = storageType
+ self.sfa = sfa
+ self.uriHelper = uriHelper
+ self.scriptContext = scriptContext
+ self.modules = {}
+ self.rootUrl = None
+ self.mapPackageName2Path = None
+
+ def getTransientPartFromUrl( self, url ):
+ rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
+ return rest[0:rest.find("/")]
+
+ def getPackageNameFromUrl( self, url ):
+ rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
+ start = rest.find("/") +1
+ return rest[start:rest.find("/",start)]
+
+
+ def removePackageByUrl( self, url ):
+ items = self.mapPackageName2Path.items()
+ for i in items:
+ if url in i[1].paths:
+ self.mapPackageName2Path.pop(i[0])
+ break
+
+ def addPackageByUrl( self, url ):
+ packageName = self.getPackageNameFromUrl( url )
+ transientPart = self.getTransientPartFromUrl( url )
+ log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl )
+ if packageName in self.mapPackageName2Path:
+ package = self.mapPackageName2Path[ packageName ]
+ package.paths = package.paths + (url, )
+ else:
+ package = Package( (url,), transientPart)
+ self.mapPackageName2Path[ packageName ] = package
+
+ def isUrlInPackage( self, url ):
+ values = self.mapPackageName2Path.values()
+ for i in values:
+# print ("checking " + url + " in " + str(i.paths))
+ if url in i.paths:
+ return True
+# print ("false")
+ return False
+
+ def setPackageAttributes( self, mapPackageName2Path, rootUrl ):
+ self.mapPackageName2Path = mapPackageName2Path
+ self.rootUrl = rootUrl
+
+ def getPersistentUrlFromStorageUrl( self, url ):
+ # package name is the second directory
+ ret = url
+ if self.rootUrl:
+ pos = len( self.rootUrl) +1
+ ret = url[0:pos]+url[url.find("/",pos)+1:len(url)]
+ log.debug( "getPersistentUrlFromStorageUrl " + url + " -> "+ ret)
+ return ret
+
+ def getStorageUrlFromPersistentUrl( self, url):
+ ret = url
+ if self.rootUrl:
+ pos = len(self.rootUrl)+1
+ packageName = url[pos:url.find("/",pos+1)]
+ package = self.mapPackageName2Path[ packageName ]
+ ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)]
+ log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret)
+ return ret
+
+ def getFuncsByUrl( self, url ):
+ src = readTextFromStream( self.sfa.openFileRead( url ) )
+ checkForPythonPathBesideScript( url[0:url.rfind('/')] )
+ src = ensureSourceState( src )
+
+ try:
+ code = ast.parse( src )
+ except:
+ log.isDebugLevel() and log.debug( "pythonscript: getFuncsByUrl: exception while parsing: " + lastException2String())
+ raise
+
+ allFuncs = []
+
+ if code is None:
+ return allFuncs
+
+ g_exportedScripts = []
+ for node in ast.iter_child_nodes(code):
+ if isinstance(node, ast.FunctionDef):
+ allFuncs.append(node.name)
+ elif isinstance(node, ast.Assign):
+ for target in node.targets:
+ try:
+ identifier = target.id
+ except AttributeError:
+ identifier = ""
+ pass
+ if identifier == "g_exportedScripts":
+ for value in node.value.elts:
+ g_exportedScripts.append(value.id)
+ return g_exportedScripts
+
+# Python 2 only
+# for node in code.node.nodes:
+# if node.__class__.__name__ == 'Function':
+# allFuncs.append(node.name)
+# elif node.__class__.__name__ == 'Assign':
+# for assignee in node.nodes:
+# if assignee.name == 'g_exportedScripts':
+# for item in node.expr.nodes:
+# if item.__class__.__name__ == 'Name':
+# g_exportedScripts.append(item.name)
+# return g_exportedScripts
+
+ return allFuncs
+
+ def getModuleByUrl( self, url ):
+ entry = self.modules.get(url)
+ load = True
+ lastRead = self.sfa.getDateTimeModified( url )
+ if entry:
+ if hasChanged( entry.lastRead, lastRead ):
+ log.debug( "file " + url + " has changed, reloading" )
+ else:
+ load = False
+
+ if load:
+ log.debug( "opening >" + url + "<" )
+
+ src = readTextFromStream( self.sfa.openFileRead( url ) )
+ checkForPythonPathBesideScript( url[0:url.rfind('/')] )
+ src = ensureSourceState( src )
+
+ # execute the module
+ entry = ModuleEntry( lastRead, types.ModuleType("ooo_script_framework") )
+ entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
+
+ code = None
+ if url.startswith( "file:" ):
+ code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" )
+ else:
+ code = compile( src, url, "exec" )
+ exec(code, entry.module.__dict__)
+ entry.module.__file__ = url
+ self.modules[ url ] = entry
+ log.debug( "mapped " + url + " to " + str( entry.module ) )
+ return entry.module
+
+#--------------------------------------------------
+def isScript( candidate ):
+ ret = False
+ if isinstance( candidate, type(isScript) ):
+ ret = True
+ return ret
+
+#-------------------------------------------------------
+class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ):
+ def __init__( self, provCtx, uri, fileName, funcName ):
+ self.fileName = fileName
+ self.funcName = funcName
+ self.provCtx = provCtx
+ self.uri = uri
+
+ def getName( self ):
+ return self.funcName
+
+ def getChildNodes(self):
+ return ()
+
+ def hasChildNodes(self):
+ return False
+
+ def getType( self):
+ return SCRIPT
+
+ def getPropertyValue( self, name ):
+ ret = None
+ try:
+ if name == "URI":
+ ret = self.provCtx.uriHelper.getScriptURI(
+ self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) )
+ elif name == "Editable" and ENABLE_EDIT_DIALOG:
+ ret = not self.provCtx.sfa.isReadOnly( self.uri )
+
+ log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
+ except:
+ log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
+ raise
+
+ return ret
+ def setPropertyValue( self, name, value ):
+ log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
+ def getPropertySetInfo( self ):
+ log.debug( "ScriptBrowseNode.getPropertySetInfo called " )
+ return None
+
+ def getIntrospection( self ):
+ return None
+
+ def invoke( self, name, params, outparamindex, outparams ):
+ if name == "Editable":
+ servicename = "com.sun.star.awt.DialogProvider"
+ ctx = self.provCtx.scriptContext.getComponentContext()
+ dlgprov = ctx.ServiceManager.createInstanceWithContext(
+ servicename, ctx )
+
+ self.editor = dlgprov.createDialog(
+ "vnd.sun.star.script:" +
+ "ScriptBindingLibrary.MacroEditor?location=application")
+
+ code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri))
+ code = ensureSourceState( code )
+ self.editor.getControl("EditorTextField").setText(code)
+
+ self.editor.getControl("RunButton").setActionCommand("Run")
+ self.editor.getControl("RunButton").addActionListener(self)
+ self.editor.getControl("SaveButton").setActionCommand("Save")
+ self.editor.getControl("SaveButton").addActionListener(self)
+
+ self.editor.execute()
+
+ return None
+
+ def actionPerformed( self, event ):
+ try:
+ if event.ActionCommand == "Run":
+ code = self.editor.getControl("EditorTextField").getText()
+ code = ensureSourceState( code )
+ mod = types.ModuleType("ooo_script_framework")
+ mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
+ exec(code, mod.__dict__)
+ values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
+ if not values:
+ values = mod.__dict__.values()
+
+ for i in values:
+ if isScript( i ):
+ i()
+ break
+
+ elif event.ActionCommand == "Save":
+ toWrite = uno.ByteSequence(
+ self.editor.getControl("EditorTextField").getText().encode(
+ sys.getdefaultencoding()) )
+ copyUrl = self.uri + ".orig"
+ self.provCtx.sfa.move( self.uri, copyUrl )
+ out = self.provCtx.sfa.openFileWrite( self.uri )
+ out.writeBytes( toWrite )
+ out.close()
+ self.provCtx.sfa.kill( copyUrl )
+# log.debug("Save is not implemented yet")
+# text = self.editor.getControl("EditorTextField").getText()
+# log.debug("Would save: " + text)
+ except:
+ # TODO: add an error box here!
+ log.error( lastException2String() )
+
+
+ def setValue( self, name, value ):
+ return None
+
+ def getValue( self, name ):
+ return None
+
+ def hasMethod( self, name ):
+ return False
+
+ def hasProperty( self, name ):
+ return False
+
+
+#-------------------------------------------------------
+class FileBrowseNode( unohelper.Base, XBrowseNode ):
+ def __init__( self, provCtx, uri , name ):
+ self.provCtx = provCtx
+ self.uri = uri
+ self.name = name
+ self.funcnames = None
+
+ def getName( self ):
+ return self.name
+
+ def getChildNodes(self):
+ ret = ()
+ try:
+ self.funcnames = self.provCtx.getFuncsByUrl( self.uri )
+
+ scriptNodeList = []
+ for i in self.funcnames:
+ scriptNodeList.append(
+ ScriptBrowseNode(
+ self.provCtx, self.uri, self.name, i ))
+ ret = tuple( scriptNodeList )
+ log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri )
+ except:
+ text = lastException2String()
+ log.error( "Error while evaluating " + self.uri + ":" + text )
+ raise
+ return ret
+
+ def hasChildNodes(self):
+ try:
+ return len(self.getChildNodes()) > 0
+ except:
+ return False
+
+ def getType( self):
+ return CONTAINER
+
+
+
+class DirBrowseNode( unohelper.Base, XBrowseNode ):
+ def __init__( self, provCtx, name, rootUrl ):
+ self.provCtx = provCtx
+ self.name = name
+ self.rootUrl = rootUrl
+
+ def getName( self ):
+ return self.name
+
+ def getChildNodes( self ):
+ try:
+ log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
+ contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
+ browseNodeList = []
+ for i in contents:
+ if i.endswith( ".py" ):
+ log.debug( "adding filenode " + i )
+ browseNodeList.append(
+ FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) )
+ elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
+ log.debug( "adding DirBrowseNode " + i )
+ browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i))
+ return tuple( browseNodeList )
+ except Exception as e:
+ text = lastException2String()
+ log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
+ log.error( text)
+ return ()
+
+ def hasChildNodes( self ):
+ return True
+
+ def getType( self ):
+ return CONTAINER
+
+ def getScript( self, uri ):
+ log.debug( "DirBrowseNode getScript " + uri + " invoked" )
+ raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
+
+
+class ManifestHandler( XDocumentHandler, unohelper.Base ):
+ def __init__( self, rootUrl ):
+ self.rootUrl = rootUrl
+
+ def startDocument( self ):
+ self.urlList = []
+
+ def endDocument( self ):
+ pass
+
+ def startElement( self , name, attlist):
+ if name == "manifest:file-entry":
+ if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
+ self.urlList.append(
+ self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
+
+ def endElement( self, name ):
+ pass
+
+ def characters ( self, chars ):
+ pass
+
+ def ignoreableWhitespace( self, chars ):
+ pass
+
+ def setDocumentLocator( self, locator ):
+ pass
+
+def isPyFileInPath( sfa, path ):
+ ret = False
+ contents = sfa.getFolderContents( path, True )
+ for i in contents:
+ if sfa.isFolder(i):
+ ret = isPyFileInPath(sfa,i)
+ else:
+ if i.endswith(".py"):
+ ret = True
+ if ret:
+ break
+ return ret
+
+# extracts META-INF directory from
+def getPathsFromPackage( rootUrl, sfa ):
+ ret = ()
+ try:
+ fileUrl = rootUrl + "/META-INF/manifest.xml"
+ inputStream = sfa.openFileRead( fileUrl )
+ parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
+ handler = ManifestHandler( rootUrl )
+ parser.setDocumentHandler( handler )
+ parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
+ for i in tuple(handler.urlList):
+ if not isPyFileInPath( sfa, i ):
+ handler.urlList.remove(i)
+ ret = tuple( handler.urlList )
+ except UnoException:
+ text = lastException2String()
+ log.debug( "getPathsFromPackage " + fileUrl + " Exception: " +text )
+ pass
+ return ret
+
+
+class Package:
+ def __init__( self, paths, transientPathElement ):
+ self.paths = paths
+ self.transientPathElement = transientPathElement
+
+class DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
+ def __init__( self ):
+ pass
+ def handle( self, event):
+ log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
+
+class DummyProgressHandler( unohelper.Base, XProgressHandler ):
+ def __init__( self ):
+ pass
+
+ def push( self,status ):
+ log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
+ def update( self,status ):
+ log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
+ def pop( self, event ):
+ log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
+
+class CommandEnvironment(unohelper.Base, XCommandEnvironment):
+ def __init__( self ):
+ self.progressHandler = DummyProgressHandler()
+ self.interactionHandler = DummyInteractionHandler()
+ def getInteractionHandler( self ):
+ return self.interactionHandler
+ def getProgressHandler( self ):
+ return self.progressHandler
+
+#maybe useful for debugging purposes
+#class ModifyListener( unohelper.Base, XModifyListener ):
+# def __init__( self ):
+# pass
+# def modified( self, event ):
+# log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
+# def disposing( self, event ):
+# log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
+
+def getModelFromDocUrl(ctx, url):
+ """Get document model from document url."""
+ doc = None
+ args = ("Local", "Office")
+ ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
+ "com.sun.star.ucb.UniversalContentBroker", args, ctx)
+ identifier = ucb.createContentIdentifier(url)
+ content = ucb.queryContent(identifier)
+ p = Property()
+ p.Name = "DocumentModel"
+ p.Handle = -1
+
+ c = Command()
+ c.Handle = -1
+ c.Name = "getPropertyValues"
+ c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
+
+ env = CommandEnvironment()
+ try:
+ ret = content.execute(c, 0, env)
+ doc = ret.getObject(1, None)
+ except Exception as e:
+ log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
+ return doc
+
+def mapStorageType2PackageContext( storageType ):
+ ret = storageType
+ if( storageType == "share:uno_packages" ):
+ ret = "shared"
+ if( storageType == "user:uno_packages" ):
+ ret = "user"
+ return ret
+
+def getPackageName2PathMap( sfa, storageType ):
+ ret = {}
+ packageManagerFactory = uno.getComponentContext().getValueByName(
+ "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
+ packageManager = packageManagerFactory.getPackageManager(
+ mapStorageType2PackageContext(storageType))
+# packageManager.addModifyListener( ModifyListener() )
+ log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
+ packages = packageManager.getDeployedPackages(
+ packageManager.createAbortChannel(), CommandEnvironment( ) )
+ log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
+
+ for i in packages:
+ log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
+ transientPathElement = penultimateElement( i.URL )
+ j = expandUri( i.URL )
+ paths = getPathsFromPackage( j, sfa )
+ if len( paths ) > 0:
+ # map package name to url, we need this later
+ log.debug( "adding Package " + transientPathElement + " " + str( paths ) )
+ ret[ lastElement( j ) ] = Package( paths, transientPathElement )
+ return ret
+
+def penultimateElement( aStr ):
+ lastSlash = aStr.rindex("/")
+ penultimateSlash = aStr.rindex("/",0,lastSlash-1)
+ return aStr[ penultimateSlash+1:lastSlash ]
+
+def lastElement( aStr):
+ return aStr[ aStr.rfind( "/" )+1:len(aStr)]
+
+class PackageBrowseNode( unohelper.Base, XBrowseNode ):
+ def __init__( self, provCtx, name, rootUrl ):
+ self.provCtx = provCtx
+ self.name = name
+ self.rootUrl = rootUrl
+
+ def getName( self ):
+ return self.name
+
+ def getChildNodes( self ):
+ items = self.provCtx.mapPackageName2Path.items()
+ browseNodeList = []
+ for i in items:
+ if len( i[1].paths ) == 1:
+ browseNodeList.append(
+ DirBrowseNode( self.provCtx, i[0], i[1].paths[0] ))
+ else:
+ for j in i[1].paths:
+ browseNodeList.append(
+ DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) )
+ return tuple( browseNodeList )
+
+ def hasChildNodes( self ):
+ return len( self.provCtx.mapPackageName2Path ) > 0
+
+ def getType( self ):
+ return CONTAINER
+
+ def getScript( self, uri ):
+ log.debug( "PackageBrowseNode getScript " + uri + " invoked" )
+ raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
+
+
+
+
+class PythonScript( unohelper.Base, XScript ):
+ def __init__( self, func, mod, args ):
+ self.func = func
+ self.mod = mod
+ self.args = args
+
+ def invoke(self, args, out, outindex ):
+ log.debug( "PythonScript.invoke " + str( args ) )
+ try:
+ if (self.args):
+ args += self.args
+ ret = self.func( *args )
+ except UnoException as e:
+ # UNO Exception continue to fly ...
+ text = lastException2String()
+ complete = "Error during invoking function " + \
+ str(self.func.__name__) + " in module " + \
+ self.mod.__file__ + " (" + text + ")"
+ log.debug( complete )
+ # some people may beat me up for modifying the exception text,
+ # but otherwise office just shows
+ # the type name and message text with no more information,
+ # this is really bad for most users.
+ e.Message = e.Message + " (" + complete + ")"
+ raise
+ except Exception as e:
+ # General python exception are converted to uno RuntimeException
+ text = lastException2String()
+ complete = "Error during invoking function " + \
+ str(self.func.__name__) + " in module " + \
+ self.mod.__file__ + " (" + text + ")"
+ log.debug( complete )
+ raise RuntimeException( complete , self )
+ log.debug( "PythonScript.invoke ret = " + str( ret ) )
+ return ret, (), ()
+
+def expandUri( uri ):
+ if uri.startswith( "vnd.sun.star.expand:" ):
+ uri = uri.replace( "vnd.sun.star.expand:", "",1)
+ uri = uno.getComponentContext().getByName(
+ "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( unquote(uri) )
+ if uri.startswith( "file:" ):
+ uri = uno.absolutize("",uri) # necessary to get rid of .. in uri
+ return uri
+
+#--------------------------------------------------------------
+class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer):
+ def __init__( self, ctx, *args ):
+ if log.isDebugLevel():
+ mystr = ""
+ for i in args:
+ if len(mystr) > 0:
+ mystr = mystr +","
+ mystr = mystr + str(i)
+ log.debug( "Entering PythonScriptProvider.ctor" + mystr )
+
+ doc = None
+ inv = None
+ storageType = ""
+
+ if isinstance(args[0], str):
+ storageType = args[0]
+ if storageType.startswith( "vnd.sun.star.tdoc" ):
+ doc = getModelFromDocUrl(ctx, storageType)
+ else:
+ inv = args[0]
+ try:
+ doc = inv.ScriptContainer
+ content = ctx.getServiceManager().createInstanceWithContext(
+ "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
+ ctx).createDocumentContent(doc)
+ storageType = content.getIdentifier().getContentIdentifier()
+ except Exception as e:
+ text = lastException2String()
+ log.error( text )
+
+ isPackage = storageType.endswith( ":uno_packages" )
+
+ try:
+# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
+# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
+ urlHelper = MyUriHelper( ctx, storageType )
+ log.debug( "got urlHelper " + str( urlHelper ) )
+
+ rootUrl = expandUri( urlHelper.getRootStorageURI() )
+ log.debug( storageType + " transformed to " + rootUrl )
+
+ ucbService = "com.sun.star.ucb.SimpleFileAccess"
+ sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
+ if not sfa:
+ log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
+ raise RuntimeException(
+ "PythonScriptProvider couldn't instantiate " +ucbService, self)
+ self.provCtx = ProviderContext(
+ storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) )
+ if isPackage:
+ mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
+ self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
+ self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
+ else:
+ self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
+
+ except Exception as e:
+ text = lastException2String()
+ log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
+ raise e
+
+ def getName( self ):
+ return self.dirBrowseNode.getName()
+
+ def getChildNodes( self ):
+ return self.dirBrowseNode.getChildNodes()
+
+ def hasChildNodes( self ):
+ return self.dirBrowseNode.hasChildNodes()
+
+ def getType( self ):
+ return self.dirBrowseNode.getType()
+
+ # retrieve function args in parenthesis
+ def getFunctionArguments(self, func_signature):
+ nOpenParenthesis = func_signature.find( "(" )
+ if -1 == nOpenParenthesis:
+ function_name = func_signature
+ arguments = None
+ else:
+ function_name = func_signature[0:nOpenParenthesis]
+ arg_part = func_signature[nOpenParenthesis+1:len(func_signature)]
+ nCloseParenthesis = arg_part.find( ")" )
+ if -1 == nCloseParenthesis:
+ raise IllegalArgumentException( "PythonLoader: mismatch parenthesis " + func_signature, self, 0 )
+ arguments = arg_part[0:nCloseParenthesis].strip()
+ if arguments == "":
+ arguments = None
+ else:
+ arguments = tuple([x.strip().strip('"') for x in arguments.split(",")])
+ return function_name, arguments
+
+ def getScript( self, scriptUri ):
+ try:
+ log.debug( "getScript " + scriptUri + " invoked")
+
+ storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
+ self.provCtx.uriHelper.getStorageURI(scriptUri) );
+ log.debug( "getScript: storageUri = " + storageUri)
+ fileUri = storageUri[0:storageUri.find( "$" )]
+ funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]
+
+ # retrieve arguments in parenthesis
+ funcName, funcArgs = self.getFunctionArguments(funcName)
+ log.debug( " getScript : parsed funcname " + str(funcName) )
+ log.debug( " getScript : func args " + str(funcArgs) )
+
+ mod = self.provCtx.getModuleByUrl( fileUri )
+ log.debug( " got mod " + str(mod) )
+
+ func = mod.__dict__[ funcName ]
+
+ log.debug( "got func " + str( func ) )
+ return PythonScript( func, mod, funcArgs )
+ except:
+ text = lastException2String()
+ log.error( text )
+ raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
+
+
+ # XServiceInfo
+ def getSupportedServices( self ):
+ return g_ImplementationHelper.getSupportedServices(g_implName)
+
+ def supportsService( self, ServiceName ):
+ return g_ImplementationHelper.supportsService( g_implName, ServiceName )
+
+ def getImplementationName(self):
+ return g_implName
+
+ def getByName( self, name ):
+ log.debug( "getByName called" + str( name ))
+ return None
+
+
+ def getElementNames( self ):
+ log.debug( "getElementNames called")
+ return ()
+
+ def hasByName( self, name ):
+ try:
+ log.debug( "hasByName called " + str( name ))
+ uri = expandUri(name)
+ ret = self.provCtx.isUrlInPackage( uri )
+ log.debug( "hasByName " + uri + " " +str( ret ) )
+ return ret
+ except:
+ text = lastException2String()
+ log.debug( "Error in hasByName:" + text )
+ return False
+
+ def removeByName( self, name ):
+ log.debug( "removeByName called" + str( name ))
+ uri = expandUri( name )
+ if self.provCtx.isUrlInPackage( uri ):
+ self.provCtx.removePackageByUrl( uri )
+ else:
+ log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
+ raise NoSuchElementException( uri + "is not in package" , self )
+ log.debug( "removeByName called" + str( uri ) + " successful" )
+
+ def insertByName( self, name, value ):
+ log.debug( "insertByName called " + str( name ) + " " + str( value ))
+ uri = expandUri( name )
+ if isPyFileInPath( self.provCtx.sfa, uri ):
+ self.provCtx.addPackageByUrl( uri )
+ else:
+ # package is no python package ...
+ log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
+ raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
+ log.debug( "insertByName called " + str( uri ) + " successful" )
+
+ def replaceByName( self, name, value ):
+ log.debug( "replaceByName called " + str( name ) + " " + str( value ))
+ uri = expandUri( name )
+ self.removeByName( name )
+ self.insertByName( name, value )
+ log.debug( "replaceByName called" + str( uri ) + " successful" )
+
+ def getElementType( self ):
+ log.debug( "getElementType called" )
+ return uno.getTypeByName( "void" )
+
+ def hasElements( self ):
+ log.debug( "hasElements got called")
+ return False
+
+g_ImplementationHelper.addImplementation( \
+ PythonScriptProvider,g_implName, \
+ ("com.sun.star.script.provider.LanguageScriptProvider",
+ "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
+
+
+log.debug( "pythonscript finished initializing" )
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/scripting/source/pyprov/scriptproviderforpython.rdb b/scripting/source/pyprov/scriptproviderforpython.rdb
new file mode 100644
index 0000000000..09a37e4da1
--- /dev/null
+++ b/scripting/source/pyprov/scriptproviderforpython.rdb
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+ * 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 .
+-->
+<components xmlns="http://openoffice.org/2010/uno-components">
+ <component loader="com.sun.star.loader.Python"
+ uri="vnd.openoffice.pymodule:pythonscript">
+ <implementation
+ name="org.libreoffice.pyuno.LanguageScriptProviderForPython">
+ <service name="com.sun.star.script.provider.ScriptProviderForPython"/>
+ <service name="com.sun.star.script.provider.LanguageScriptProvider"/>
+ </implementation>
+ </component>
+</components>
diff --git a/scripting/source/stringresource/stringresource.component b/scripting/source/stringresource/stringresource.component
new file mode 100644
index 0000000000..a2b2a24f33
--- /dev/null
+++ b/scripting/source/stringresource/stringresource.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.scripting.StringResource"
+ constructor="scripting_StringResourcePersistenceImpl_implementation">
+ <service name="com.sun.star.resource.StringResource"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.scripting.StringResourceWithLocation"
+ constructor="scripting_StringResourceWithLocationImpl_get_implementation">
+ <service name="com.sun.star.resource.StringResourceWithLocation"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.scripting.StringResourceWithStorage"
+ constructor="scripting_StringResourceWithStorageImpl_get_implementation">
+ <service name="com.sun.star.resource.StringResourceWithStorage"/>
+ </implementation>
+</component>
diff --git a/scripting/source/stringresource/stringresource.cxx b/scripting/source/stringresource/stringresource.cxx
new file mode 100644
index 0000000000..79090c6eea
--- /dev/null
+++ b/scripting/source/stringresource/stringresource.cxx
@@ -0,0 +1,2584 @@
+/* -*- 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 <memory>
+#include "stringresource.hxx"
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/io/TextInputStream.hpp>
+#include <com/sun/star/io/TextOutputStream.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/resource/MissingResourceException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/ustrbuf.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/tempfile.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::embed;
+using namespace ::com::sun::star::container;
+
+
+namespace stringresource
+{
+
+// StringResourceImpl
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_StringResourcePersistenceImpl_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new StringResourcePersistenceImpl(context));
+}
+
+
+StringResourceImpl::StringResourceImpl( const Reference< XComponentContext >& rxContext )
+ : m_xContext( rxContext )
+ , m_pCurrentLocaleItem( nullptr )
+ , m_pDefaultLocaleItem( nullptr )
+ , m_bDefaultModified( false )
+ , m_bModified( false )
+ , m_bReadOnly( false )
+ , m_nNextUniqueNumericId( UNIQUE_NUMBER_NEEDS_INITIALISATION )
+{
+}
+
+
+StringResourceImpl::~StringResourceImpl()
+{
+}
+
+
+// XServiceInfo
+
+OUString StringResourceImpl::getImplementationName( )
+{
+ return "com.sun.star.comp.scripting.StringResource";
+}
+
+sal_Bool StringResourceImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > StringResourceImpl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.resource.StringResource" };
+}
+
+
+// XModifyBroadcaster
+
+void StringResourceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
+{
+ if( !aListener.is() )
+ throw RuntimeException();
+
+ std::unique_lock aGuard( m_aMutex );
+ m_aListenerContainer.addInterface( aGuard, aListener );
+}
+
+void StringResourceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
+{
+ if( !aListener.is() )
+ throw RuntimeException();
+
+ std::unique_lock aGuard( m_aMutex );
+ m_aListenerContainer.removeInterface( aGuard, aListener );
+}
+
+
+// XStringResourceResolver
+
+OUString StringResourceImpl::implResolveString
+ ( const OUString& ResourceID, LocaleItem* pLocaleItem )
+{
+ OUString aRetStr;
+ bool bSuccess = false;
+ if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
+ {
+ IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
+ if( it != pLocaleItem->m_aIdToStringMap.end() )
+ {
+ aRetStr = (*it).second;
+ bSuccess = true;
+ }
+ }
+ if( !bSuccess )
+ {
+ throw css::resource::MissingResourceException( "StringResourceImpl: No entry for ResourceID: " + ResourceID );
+ }
+ return aRetStr;
+}
+
+OUString StringResourceImpl::resolveString( const OUString& ResourceID )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return implResolveString( ResourceID, m_pCurrentLocaleItem );
+}
+
+OUString StringResourceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ LocaleItem* pLocaleItem = getItemForLocale( locale, false );
+ return implResolveString( ResourceID, pLocaleItem );
+}
+
+bool StringResourceImpl::implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem )
+{
+ bool bSuccess = false;
+ if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
+ {
+ IdToStringMap::iterator it = pLocaleItem->m_aIdToStringMap.find( ResourceID );
+ if( it != pLocaleItem->m_aIdToStringMap.end() )
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+sal_Bool StringResourceImpl::hasEntryForId( const OUString& ResourceID )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return implHasEntryForId( ResourceID, m_pCurrentLocaleItem );
+}
+
+sal_Bool StringResourceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
+ const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ LocaleItem* pLocaleItem = getItemForLocale( locale, false );
+ return implHasEntryForId( ResourceID, pLocaleItem );
+}
+
+Sequence< OUString > StringResourceImpl::implGetResourceIDs( LocaleItem* pLocaleItem )
+{
+ Sequence< OUString > aIDSeq( 0 );
+ if( pLocaleItem && loadLocale( pLocaleItem ) )
+ {
+ const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
+ sal_Int32 nResourceIDCount = rHashMap.size();
+ aIDSeq.realloc( nResourceIDCount );
+ OUString* pStrings = aIDSeq.getArray();
+
+ int iTarget = 0;
+ for( const auto& rEntry : rHashMap )
+ {
+ pStrings[iTarget] = rEntry.first;
+ iTarget++;
+ }
+ }
+ return aIDSeq;
+}
+
+Sequence< OUString > StringResourceImpl::getResourceIDsForLocale
+ ( const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ LocaleItem* pLocaleItem = getItemForLocale( locale, false );
+ return implGetResourceIDs( pLocaleItem );
+}
+
+Sequence< OUString > StringResourceImpl::getResourceIDs( )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return implGetResourceIDs( m_pCurrentLocaleItem );
+}
+
+Locale StringResourceImpl::getCurrentLocale()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ Locale aRetLocale;
+ if( m_pCurrentLocaleItem != nullptr )
+ aRetLocale = m_pCurrentLocaleItem->m_locale;
+ return aRetLocale;
+}
+
+Locale StringResourceImpl::getDefaultLocale( )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ Locale aRetLocale;
+ if( m_pDefaultLocaleItem != nullptr )
+ aRetLocale = m_pDefaultLocaleItem->m_locale;
+ return aRetLocale;
+}
+
+Sequence< Locale > StringResourceImpl::getLocales( )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ sal_Int32 nSize = m_aLocaleItemVector.size();
+ Sequence< Locale > aLocalSeq( nSize );
+ Locale* pLocales = aLocalSeq.getArray();
+ int iTarget = 0;
+ for( const auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ pLocales[iTarget] = pLocaleItem->m_locale;
+ iTarget++;
+ }
+ return aLocalSeq;
+}
+
+
+// XStringResourceManager
+
+void StringResourceImpl::implCheckReadOnly( const char* pExceptionMsg )
+{
+ if( m_bReadOnly )
+ {
+ OUString errorMsg = OUString::createFromAscii( pExceptionMsg );
+ throw NoSupportException( errorMsg );
+ }
+}
+
+sal_Bool StringResourceImpl::isReadOnly()
+{
+ return m_bReadOnly;
+}
+
+void StringResourceImpl::implSetCurrentLocale( std::unique_lock<std::mutex>& rGuard, const Locale& locale,
+ bool FindClosestMatch, bool bUseDefaultIfNoMatch )
+{
+ LocaleItem* pLocaleItem = nullptr;
+ if( FindClosestMatch )
+ pLocaleItem = getClosestMatchItemForLocale( locale );
+ else
+ pLocaleItem = getItemForLocale( locale, true );
+
+ if( pLocaleItem == nullptr && bUseDefaultIfNoMatch )
+ pLocaleItem = m_pDefaultLocaleItem;
+
+ if( pLocaleItem != nullptr )
+ {
+ (void)loadLocale( pLocaleItem );
+ m_pCurrentLocaleItem = pLocaleItem;
+
+ // Only notify without modifying
+ implNotifyListeners(rGuard);
+ }
+}
+
+void StringResourceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implSetCurrentLocale( aGuard, locale, FindClosestMatch, false/*bUseDefaultIfNoMatch*/ );
+}
+
+void StringResourceImpl::setDefaultLocale( const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::setDefaultLocale(): Read only" );
+
+ LocaleItem* pLocaleItem = getItemForLocale( locale, true );
+ if( pLocaleItem && pLocaleItem != m_pDefaultLocaleItem )
+ {
+ if( m_pDefaultLocaleItem )
+ {
+ m_aChangedDefaultLocaleVector.push_back(
+ std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
+ }
+
+ m_pDefaultLocaleItem = pLocaleItem;
+ m_bDefaultModified = true;
+ implModified(aGuard);
+ }
+}
+
+void StringResourceImpl::implSetString( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID,
+ const OUString& Str, LocaleItem* pLocaleItem )
+{
+ if( !(pLocaleItem != nullptr && loadLocale( pLocaleItem )) )
+ return;
+
+ IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
+
+ IdToStringMap::iterator it = rHashMap.find( ResourceID );
+ bool bNew = ( it == rHashMap.end() );
+ if( bNew )
+ {
+ IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
+ rIndexMap[ ResourceID ] = pLocaleItem->m_nNextIndex++;
+ implScanIdForNumber( ResourceID );
+ }
+ rHashMap[ ResourceID ] = Str;
+ pLocaleItem->m_bModified = true;
+ implModified(rGuard);
+}
+
+void StringResourceImpl::setString( const OUString& ResourceID, const OUString& Str )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::setString(): Read only" );
+ implSetString( aGuard, ResourceID, Str, m_pCurrentLocaleItem );
+}
+
+void StringResourceImpl::setStringForLocale
+ ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::setStringForLocale(): Read only" );
+ LocaleItem* pLocaleItem = getItemForLocale( locale, false );
+ implSetString( aGuard, ResourceID, Str, pLocaleItem );
+}
+
+void StringResourceImpl::implRemoveId( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem )
+{
+ if( pLocaleItem != nullptr && loadLocale( pLocaleItem ) )
+ {
+ IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
+ IdToStringMap::iterator it = rHashMap.find( ResourceID );
+ if( it == rHashMap.end() )
+ {
+ throw css::resource::MissingResourceException( "StringResourceImpl: No entries for ResourceID: " + ResourceID );
+ }
+ rHashMap.erase( it );
+ pLocaleItem->m_bModified = true;
+ implModified(rGuard);
+ }
+}
+
+void StringResourceImpl::removeId( const OUString& ResourceID )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::removeId(): Read only" );
+ implRemoveId( aGuard, ResourceID, m_pCurrentLocaleItem );
+}
+
+void StringResourceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::removeIdForLocale(): Read only" );
+ LocaleItem* pLocaleItem = getItemForLocale( locale, false );
+ implRemoveId( aGuard, ResourceID, pLocaleItem );
+}
+
+void StringResourceImpl::newLocale( const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::newLocale(): Read only" );
+
+ if( getItemForLocale( locale, false ) != nullptr )
+ {
+ throw ElementExistException( "StringResourceImpl: locale already exists" );
+ }
+
+ // TODO?: Check if locale is valid? How?
+ //if (!bValid)
+ //{
+ // OUString errorMsg("StringResourceImpl: Invalid locale");
+ // throw IllegalArgumentException( errorMsg, Reference< XInterface >(), 0 );
+ //}
+
+ LocaleItem* pLocaleItem = new LocaleItem( locale );
+ m_aLocaleItemVector.emplace_back( pLocaleItem );
+ pLocaleItem->m_bModified = true;
+
+ // Copy strings from default locale
+ LocaleItem* pCopyFromItem = m_pDefaultLocaleItem;
+ if( pCopyFromItem == nullptr )
+ pCopyFromItem = m_pCurrentLocaleItem;
+ if( pCopyFromItem != nullptr && loadLocale( pCopyFromItem ) )
+ {
+ const IdToStringMap& rSourceMap = pCopyFromItem->m_aIdToStringMap;
+ IdToStringMap& rTargetMap = pLocaleItem->m_aIdToStringMap;
+ for( const auto& rEntry : rSourceMap )
+ {
+ OUString aId = rEntry.first;
+ OUString aStr = rEntry.second;
+ rTargetMap[ aId ] = aStr;
+ }
+
+ const IdToIndexMap& rSourceIndexMap = pCopyFromItem->m_aIdToIndexMap;
+ IdToIndexMap& rTargetIndexMap = pLocaleItem->m_aIdToIndexMap;
+ for( const auto& rIndex : rSourceIndexMap )
+ {
+ OUString aId = rIndex.first;
+ sal_Int32 nIndex = rIndex.second;
+ rTargetIndexMap[ aId ] = nIndex;
+ }
+ pLocaleItem->m_nNextIndex = pCopyFromItem->m_nNextIndex;
+ }
+
+ if( m_pCurrentLocaleItem == nullptr )
+ m_pCurrentLocaleItem = pLocaleItem;
+
+ if( m_pDefaultLocaleItem == nullptr )
+ {
+ m_pDefaultLocaleItem = pLocaleItem;
+ m_bDefaultModified = true;
+ }
+
+ implModified(aGuard);
+}
+
+void StringResourceImpl::removeLocale( const Locale& locale )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceImpl::removeLocale(): Read only" );
+
+ LocaleItem* pRemoveItem = getItemForLocale( locale, true );
+ if( !pRemoveItem )
+ return;
+
+ // Last locale?
+ sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
+ if( nLocaleCount > 1 )
+ {
+ if( m_pCurrentLocaleItem == pRemoveItem ||
+ m_pDefaultLocaleItem == pRemoveItem )
+ {
+ LocaleItem* pFallbackItem = nullptr;
+ for( const auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ if( pLocaleItem.get() != pRemoveItem )
+ {
+ pFallbackItem = pLocaleItem.get();
+ break;
+ }
+ }
+ if( m_pCurrentLocaleItem == pRemoveItem )
+ {
+ setCurrentLocale( pFallbackItem->m_locale, false/*FindClosestMatch*/ );
+ }
+ if( m_pDefaultLocaleItem == pRemoveItem )
+ {
+ setDefaultLocale( pFallbackItem->m_locale );
+ }
+ }
+ }
+ auto it = std::find_if(m_aLocaleItemVector.begin(), m_aLocaleItemVector.end(),
+ [&pRemoveItem](const std::unique_ptr<LocaleItem>& rxItem) { return rxItem.get() == pRemoveItem; });
+ if (it == m_aLocaleItemVector.end())
+ return;
+
+ // Remember locale item to delete file while storing
+ m_aDeletedLocaleItemVector.push_back( std::move(*it) );
+
+ // Last locale?
+ if( nLocaleCount == 1 )
+ {
+ m_nNextUniqueNumericId = 0;
+ if( m_pDefaultLocaleItem )
+ {
+ m_aChangedDefaultLocaleVector.push_back(
+ std::make_unique<LocaleItem>( m_pDefaultLocaleItem->m_locale ) );
+ }
+ m_pCurrentLocaleItem = nullptr;
+ m_pDefaultLocaleItem = nullptr;
+ }
+
+ m_aLocaleItemVector.erase( it );
+
+ implModified(aGuard);
+}
+
+void StringResourceImpl::implScanIdForNumber( const OUString& ResourceID )
+{
+ const sal_Unicode* pSrc = ResourceID.getStr();
+ sal_Int32 nLen = ResourceID.getLength();
+
+ sal_Int32 nNumber = 0;
+ for( sal_Int32 i = 0 ; i < nLen ; i++ )
+ {
+ sal_Unicode c = pSrc[i];
+ if( c >= '0' && c <= '9' )
+ {
+ sal_uInt16 nDigitVal = c - '0';
+ nNumber = 10*nNumber + nDigitVal;
+ }
+ else
+ break;
+ }
+
+ if( m_nNextUniqueNumericId < nNumber + 1 )
+ m_nNextUniqueNumericId = nNumber + 1;
+}
+
+sal_Int32 StringResourceImpl::getUniqueNumericId( )
+{
+ if( m_nNextUniqueNumericId == UNIQUE_NUMBER_NEEDS_INITIALISATION )
+ {
+ implLoadAllLocales();
+ m_nNextUniqueNumericId = 0;
+ }
+
+ if( m_nNextUniqueNumericId < UNIQUE_NUMBER_NEEDS_INITIALISATION )
+ {
+ throw NoSupportException( "getUniqueNumericId: Extended sal_Int32 range" );
+ }
+ return m_nNextUniqueNumericId;
+}
+
+
+// Private helper methods
+
+LocaleItem* StringResourceImpl::getItemForLocale
+ ( const Locale& locale, bool bException )
+{
+ LocaleItem* pRetItem = nullptr;
+
+ // Search for locale
+ for( auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ if( pLocaleItem )
+ {
+ Locale& cmp_locale = pLocaleItem->m_locale;
+ if( cmp_locale.Language == locale.Language &&
+ cmp_locale.Country == locale.Country &&
+ cmp_locale.Variant == locale.Variant )
+ {
+ pRetItem = pLocaleItem.get();
+ break;
+ }
+ }
+ }
+
+ if( pRetItem == nullptr && bException )
+ {
+ throw IllegalArgumentException( "StringResourceImpl: Invalid locale", Reference< XInterface >(), 0 );
+ }
+ return pRetItem;
+}
+
+// Returns the LocaleItem for a given locale, if it exists, otherwise NULL.
+// This method performs a closest match search, at least the language must match.
+LocaleItem* StringResourceImpl::getClosestMatchItemForLocale( const Locale& locale )
+{
+ LocaleItem* pRetItem = nullptr;
+
+ ::std::vector< Locale > aLocales( m_aLocaleItemVector.size());
+ size_t i = 0;
+ for( const auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ aLocales[i] = (pLocaleItem ? pLocaleItem->m_locale : Locale());
+ ++i;
+ }
+ ::std::vector< Locale >::const_iterator iFound( LanguageTag::getMatchingFallback( aLocales, locale));
+ if (iFound != aLocales.end())
+ pRetItem = (m_aLocaleItemVector.begin() + (iFound - aLocales.begin()))->get();
+
+ return pRetItem;
+}
+
+void StringResourceImpl::implModified(std::unique_lock<std::mutex>& rGuard)
+{
+ m_bModified = true;
+ implNotifyListeners(rGuard);
+}
+
+void StringResourceImpl::implNotifyListeners(std::unique_lock<std::mutex>& rGuard)
+{
+ EventObject aEvent;
+ aEvent.Source = getXWeak();
+ m_aListenerContainer.forEach(rGuard,
+ [&aEvent](const css::uno::Reference<XModifyListener>& xListener)
+ {
+ xListener->modified(aEvent);
+ }
+ );
+}
+
+
+// Loading
+
+bool StringResourceImpl::loadLocale( LocaleItem* )
+{
+ // Base implementation has nothing to load
+ return true;
+}
+
+void StringResourceImpl::implLoadAllLocales()
+{
+ // Base implementation has nothing to load
+}
+
+
+// StringResourcePersistenceImpl
+
+
+StringResourcePersistenceImpl::StringResourcePersistenceImpl( const Reference< XComponentContext >& rxContext )
+ : StringResourcePersistenceImpl_BASE( rxContext )
+{
+}
+
+
+StringResourcePersistenceImpl::~StringResourcePersistenceImpl()
+{
+}
+
+
+// XServiceInfo
+
+
+OUString StringResourcePersistenceImpl::getImplementationName( )
+{
+ return "com.sun.star.comp.scripting.StringResource";
+}
+
+
+sal_Bool StringResourcePersistenceImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+
+Sequence< OUString > StringResourcePersistenceImpl::getSupportedServiceNames( )
+{
+ return StringResourceImpl::getSupportedServiceNames();
+}
+
+
+// XInitialization base functionality for derived classes
+
+
+constexpr OUString aNameBaseDefaultStr = u"strings"_ustr;
+
+void StringResourcePersistenceImpl::implInitializeCommonParameters
+ ( std::unique_lock<std::mutex>& rGuard, const Sequence< Any >& aArguments )
+{
+ bool bReadOnlyOk = (aArguments[1] >>= m_bReadOnly);
+ if( !bReadOnlyOk )
+ {
+ throw IllegalArgumentException( "XInitialization::initialize: Expected ReadOnly flag", Reference< XInterface >(), 1 );
+ }
+
+ css::lang::Locale aCurrentLocale;
+ bool bLocaleOk = (aArguments[2] >>= aCurrentLocale);
+ if( !bLocaleOk )
+ {
+ throw IllegalArgumentException( "XInitialization::initialize: Expected Locale", Reference< XInterface >(), 2 );
+ }
+
+ bool bNameBaseOk = (aArguments[3] >>= m_aNameBase);
+ if( !bNameBaseOk )
+ {
+ throw IllegalArgumentException( "XInitialization::initialize: Expected NameBase string", Reference< XInterface >(), 3 );
+ }
+ if( m_aNameBase.isEmpty() )
+ m_aNameBase = aNameBaseDefaultStr;
+
+ bool bCommentOk = (aArguments[4] >>= m_aComment);
+ if( !bCommentOk )
+ {
+ throw IllegalArgumentException( "XInitialization::initialize: Expected Comment string", Reference< XInterface >(), 4 );
+ }
+
+ implScanLocales();
+
+ implSetCurrentLocale( rGuard, aCurrentLocale, true/*FindClosestMatch*/, true/*bUseDefaultIfNoMatch*/ );
+}
+
+
+// Forwarding calls to base class
+
+// XModifyBroadcaster
+void StringResourcePersistenceImpl::addModifyListener( const Reference< XModifyListener >& aListener )
+{
+ StringResourceImpl::addModifyListener( aListener );
+}
+void StringResourcePersistenceImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
+{
+ StringResourceImpl::removeModifyListener( aListener );
+}
+
+// XStringResourceResolver
+OUString StringResourcePersistenceImpl::resolveString( const OUString& ResourceID )
+{
+ return StringResourceImpl::resolveString( ResourceID ) ;
+}
+OUString StringResourcePersistenceImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
+}
+sal_Bool StringResourcePersistenceImpl::hasEntryForId( const OUString& ResourceID )
+{
+ return StringResourceImpl::hasEntryForId( ResourceID ) ;
+}
+sal_Bool StringResourcePersistenceImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
+ const Locale& locale )
+{
+ return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
+}
+Locale StringResourcePersistenceImpl::getCurrentLocale()
+{
+ return StringResourceImpl::getCurrentLocale();
+}
+Locale StringResourcePersistenceImpl::getDefaultLocale( )
+{
+ return StringResourceImpl::getDefaultLocale();
+}
+Sequence< Locale > StringResourcePersistenceImpl::getLocales( )
+{
+ return StringResourceImpl::getLocales();
+}
+
+// XStringResourceManager
+sal_Bool StringResourcePersistenceImpl::isReadOnly()
+{
+ return StringResourceImpl::isReadOnly();
+}
+void StringResourcePersistenceImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
+{
+ StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
+}
+void StringResourcePersistenceImpl::setDefaultLocale( const Locale& locale )
+{
+ StringResourceImpl::setDefaultLocale( locale );
+}
+Sequence< OUString > StringResourcePersistenceImpl::getResourceIDs( )
+{
+ return StringResourceImpl::getResourceIDs();
+}
+void StringResourcePersistenceImpl::setString( const OUString& ResourceID, const OUString& Str )
+{
+ StringResourceImpl::setString( ResourceID, Str );
+}
+void StringResourcePersistenceImpl::setStringForLocale
+ ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
+{
+ StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
+}
+Sequence< OUString > StringResourcePersistenceImpl::getResourceIDsForLocale
+ ( const Locale& locale )
+{
+ return StringResourceImpl::getResourceIDsForLocale( locale );
+}
+void StringResourcePersistenceImpl::removeId( const OUString& ResourceID )
+{
+ StringResourceImpl::removeId( ResourceID );
+}
+void StringResourcePersistenceImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ StringResourceImpl::removeIdForLocale( ResourceID, locale );
+}
+void StringResourcePersistenceImpl::newLocale( const Locale& locale )
+{
+ StringResourceImpl::newLocale( locale );
+}
+void StringResourcePersistenceImpl::removeLocale( const Locale& locale )
+{
+ StringResourceImpl::removeLocale( locale );
+}
+sal_Int32 StringResourcePersistenceImpl::getUniqueNumericId( )
+{
+ return StringResourceImpl::getUniqueNumericId();
+}
+
+
+// XStringResourcePersistence
+
+void StringResourcePersistenceImpl::store()
+{
+}
+
+sal_Bool StringResourcePersistenceImpl::isModified( )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ return m_bModified;
+}
+
+void StringResourcePersistenceImpl::setComment( const OUString& Comment )
+{
+ m_aComment = Comment;
+}
+
+void StringResourcePersistenceImpl::storeToStorage( const Reference< XStorage >& Storage,
+ const OUString& NameBase, const OUString& Comment )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ implStoreAtStorage( NameBase, Comment, Storage, false/*bUsedForStore*/, true/*bStoreAll*/ );
+}
+
+void StringResourcePersistenceImpl::implStoreAtStorage
+(
+ const OUString& aNameBase,
+ const OUString& aComment,
+ const Reference< css::embed::XStorage >& Storage,
+ bool bUsedForStore,
+ bool bStoreAll
+)
+{
+ // Delete files for deleted locales
+ if( bUsedForStore )
+ {
+ for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
+ {
+ if( pLocaleItem )
+ {
+ OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".properties";
+
+ try
+ {
+ Storage->removeElement( aStreamName );
+ }
+ catch( Exception& )
+ {}
+
+ pLocaleItem.reset();
+ }
+ }
+ m_aDeletedLocaleItemVector.clear();
+ }
+
+ for( auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ if( pLocaleItem != nullptr && (bStoreAll || pLocaleItem->m_bModified) &&
+ loadLocale( pLocaleItem.get() ) )
+ {
+ OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), aNameBase ) + ".properties";
+
+ Reference< io::XStream > xElementStream =
+ Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
+
+ uno::Reference< beans::XPropertySet > xProps( xElementStream, uno::UNO_QUERY );
+ OSL_ENSURE( xProps.is(), "The StorageStream must implement XPropertySet interface!" );
+ if ( xProps.is() )
+ {
+ OUString aPropName("MediaType");
+ xProps->setPropertyValue( aPropName, uno::Any( OUString("text/plain") ) );
+
+ aPropName = "UseCommonStoragePasswordEncryption";
+ xProps->setPropertyValue( aPropName, uno::Any( true ) );
+ }
+
+ Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
+ if( xOutputStream.is() )
+ implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
+ xOutputStream->closeOutput();
+
+ if( bUsedForStore )
+ pLocaleItem->m_bModified = false;
+ }
+ }
+
+ // Delete files for changed defaults
+ if( bUsedForStore )
+ {
+ for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
+ {
+ OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem.get(), m_aNameBase ) + ".default";
+
+ try
+ {
+ Storage->removeElement( aStreamName );
+ }
+ catch( Exception& )
+ {}
+
+ pLocaleItem.reset();
+ }
+ m_aChangedDefaultLocaleVector.clear();
+ }
+
+ // Default locale
+ if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || m_bDefaultModified)) )
+ return;
+
+ OUString aStreamName = implGetFileNameForLocaleItem( m_pDefaultLocaleItem, aNameBase ) + ".default";
+
+ Reference< io::XStream > xElementStream =
+ Storage->openStreamElement( aStreamName, ElementModes::READWRITE );
+
+ // Only create stream without content
+ Reference< io::XOutputStream > xOutputStream = xElementStream->getOutputStream();
+ xOutputStream->closeOutput();
+
+ if( bUsedForStore )
+ m_bDefaultModified = false;
+}
+
+void StringResourcePersistenceImpl::storeToURL( const OUString& URL,
+ const OUString& NameBase, const OUString& Comment,
+ const Reference< css::task::XInteractionHandler >& Handler )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ Reference< ucb::XSimpleFileAccess3 > xFileAccess = ucb::SimpleFileAccess::create(m_xContext);
+ if( xFileAccess.is() && Handler.is() )
+ xFileAccess->setInteractionHandler( Handler );
+
+ implStoreAtLocation( URL, NameBase, Comment, xFileAccess, false/*bUsedForStore*/, true/*bStoreAll*/ );
+}
+
+void StringResourcePersistenceImpl::implKillRemovedLocaleFiles
+(
+ std::u16string_view Location,
+ const OUString& aNameBase,
+ const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
+)
+{
+ // Delete files for deleted locales
+ for( auto& pLocaleItem : m_aDeletedLocaleItemVector )
+ {
+ if( pLocaleItem )
+ {
+ OUString aCompleteFileName =
+ implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
+ if( xFileAccess->exists( aCompleteFileName ) )
+ xFileAccess->kill( aCompleteFileName );
+
+ pLocaleItem.reset();
+ }
+ }
+ m_aDeletedLocaleItemVector.clear();
+}
+
+void StringResourcePersistenceImpl::implKillChangedDefaultFiles
+(
+ std::u16string_view Location,
+ const OUString& aNameBase,
+ const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
+)
+{
+ // Delete files for changed defaults
+ for( auto& pLocaleItem : m_aChangedDefaultLocaleVector )
+ {
+ OUString aCompleteFileName =
+ implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location, true );
+ if( xFileAccess->exists( aCompleteFileName ) )
+ xFileAccess->kill( aCompleteFileName );
+ pLocaleItem.reset();
+ }
+ m_aChangedDefaultLocaleVector.clear();
+}
+
+void StringResourcePersistenceImpl::implStoreAtLocation
+(
+ std::u16string_view Location,
+ const OUString& aNameBase,
+ const OUString& aComment,
+ const Reference< ucb::XSimpleFileAccess3 >& xFileAccess,
+ bool bUsedForStore,
+ bool bStoreAll,
+ bool bKillAll
+)
+{
+ // Delete files for deleted locales
+ if( bUsedForStore || bKillAll )
+ implKillRemovedLocaleFiles( Location, aNameBase, xFileAccess );
+
+ for( auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ if( pLocaleItem != nullptr && (bStoreAll || bKillAll || pLocaleItem->m_bModified) &&
+ loadLocale( pLocaleItem.get() ) )
+ {
+ OUString aCompleteFileName =
+ implGetPathForLocaleItem( pLocaleItem.get(), aNameBase, Location );
+ if( xFileAccess->exists( aCompleteFileName ) )
+ xFileAccess->kill( aCompleteFileName );
+
+ if( !bKillAll )
+ {
+ // Create Output stream
+ Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
+ if( xOutputStream.is() )
+ {
+ implWritePropertiesFile( pLocaleItem.get(), xOutputStream, aComment );
+ xOutputStream->closeOutput();
+ }
+ if( bUsedForStore )
+ pLocaleItem->m_bModified = false;
+ }
+ }
+ }
+
+ // Delete files for changed defaults
+ if( bUsedForStore || bKillAll )
+ implKillChangedDefaultFiles( Location, aNameBase, xFileAccess );
+
+ // Default locale
+ if( !(m_pDefaultLocaleItem != nullptr && (bStoreAll || bKillAll || m_bDefaultModified)) )
+ return;
+
+ OUString aCompleteFileName =
+ implGetPathForLocaleItem( m_pDefaultLocaleItem, aNameBase, Location, true );
+ if( xFileAccess->exists( aCompleteFileName ) )
+ xFileAccess->kill( aCompleteFileName );
+
+ if( !bKillAll )
+ {
+ // Create Output stream
+ Reference< io::XOutputStream > xOutputStream = xFileAccess->openFileWrite( aCompleteFileName );
+ if( xOutputStream.is() )
+ xOutputStream->closeOutput();
+
+ if( bUsedForStore )
+ m_bDefaultModified = false;
+ }
+}
+
+
+// BinaryOutput, helper class for exportBinary
+
+class BinaryOutput
+{
+ rtl::Reference< utl::TempFileFastService > m_xTempFile;
+ Reference< io::XOutputStream > m_xOutputStream;
+
+public:
+ explicit BinaryOutput();
+
+ const Reference< io::XOutputStream >& getOutputStream() const
+ { return m_xOutputStream; }
+
+ Sequence< ::sal_Int8 > closeAndGetData();
+
+ // Template to be used with sal_Int16 and sal_Unicode
+ template< class T >
+ void write16BitInt( T n );
+ void writeInt16( sal_Int16 n )
+ { write16BitInt( n ); }
+ void writeUnicodeChar( sal_Unicode n )
+ { write16BitInt( n ); }
+ void writeInt32( sal_Int32 n );
+ void writeString( const OUString& aStr );
+};
+
+BinaryOutput::BinaryOutput()
+{
+ m_xTempFile = new utl::TempFileFastService;
+ m_xOutputStream = m_xTempFile;
+}
+
+template< class T >
+void BinaryOutput::write16BitInt( T n )
+{
+ if( !m_xOutputStream.is() )
+ return;
+
+ Sequence< sal_Int8 > aSeq( 2 );
+ sal_Int8* p = aSeq.getArray();
+
+ sal_Int8 nLow = sal_Int8( n & 0xff );
+ sal_Int8 nHigh = sal_Int8( n >> 8 );
+
+ p[0] = nLow;
+ p[1] = nHigh;
+ m_xOutputStream->writeBytes( aSeq );
+}
+
+void BinaryOutput::writeInt32( sal_Int32 n )
+{
+ if( !m_xOutputStream.is() )
+ return;
+
+ Sequence< sal_Int8 > aSeq( 4 );
+ sal_Int8* p = aSeq.getArray();
+
+ for( sal_Int16 i = 0 ; i < 4 ; i++ )
+ {
+ p[i] = sal_Int8( n & 0xff );
+ n >>= 8;
+ }
+ m_xOutputStream->writeBytes( aSeq );
+}
+
+void BinaryOutput::writeString( const OUString& aStr )
+{
+ sal_Int32 nLen = aStr.getLength();
+ const sal_Unicode* pStr = aStr.getStr();
+
+ for( sal_Int32 i = 0 ; i < nLen ; i++ )
+ writeUnicodeChar( pStr[i] );
+
+ writeUnicodeChar( 0 );
+}
+
+Sequence< ::sal_Int8 > BinaryOutput::closeAndGetData()
+{
+ Sequence< ::sal_Int8 > aRetSeq;
+ if( !m_xOutputStream.is() )
+ return aRetSeq;
+
+ m_xOutputStream->closeOutput();
+
+ sal_Int32 nSize = static_cast<sal_Int32>(m_xTempFile->getPosition());
+
+ m_xTempFile->seek( 0 );
+ sal_Int32 nRead = m_xTempFile->readBytes( aRetSeq, nSize );
+ OSL_ENSURE( nRead == nSize, "BinaryOutput::closeAndGetData: nRead != nSize" );
+
+ return aRetSeq;
+}
+
+
+// Binary format:
+
+// Header
+// Byte Content
+// 0 + 1 sal_Int16: Version, currently 0, low byte first
+// 2 + 3 sal_Int16: Locale count = n, low byte first
+// 4 + 5 sal_Int16: Default Locale position in Locale list, == n if none
+// 6 - 7 sal_Int32: Start index locale block 0, lowest byte first
+// (n-1) * sal_Int32: Start index locale block 1 to n, lowest byte first
+// 6 + 4*n sal_Int32: "Start index" non existing locale block n+1,
+// marks the first invalid index, kind of EOF
+
+// Locale block
+// All strings are stored as 2-Byte-0 terminated sequence
+// of 16 bit Unicode characters, each with low byte first
+// Empty strings only contain the 2-Byte-0
+
+// Members of com.sun.star.lang.Locale
+// with l1 = Locale.Language.getLength()
+// with l2 = Locale.Country.getLength()
+// with l3 = Locale.Variant.getLength()
+// pos0 = 0 Locale.Language
+// pos1 = 2 * (l1 + 1) Locale.Country
+// pos2 = pos1 + 2 * (l2 + 1) Locale.Variant
+// pos3 = pos2 + 2 * (l3 + 1)
+// pos3 Properties file written by implWritePropertiesFile
+
+Sequence< sal_Int8 > StringResourcePersistenceImpl::exportBinary( )
+{
+ BinaryOutput aOut;
+
+ sal_Int32 nLocaleCount = m_aLocaleItemVector.size();
+ std::vector<Sequence< sal_Int8 >> aLocaleDataSeq(nLocaleCount);
+
+ sal_Int32 iLocale = 0;
+ sal_Int32 iDefault = 0;
+ for( auto& pLocaleItem : m_aLocaleItemVector )
+ {
+ if( pLocaleItem != nullptr && loadLocale( pLocaleItem.get() ) )
+ {
+ if( m_pDefaultLocaleItem == pLocaleItem.get() )
+ iDefault = iLocale;
+
+ BinaryOutput aLocaleOut;
+ implWriteLocaleBinary( pLocaleItem.get(), aLocaleOut );
+
+ aLocaleDataSeq[iLocale] = aLocaleOut.closeAndGetData();
+ }
+ ++iLocale;
+ }
+
+ // Write header
+ sal_Int16 nLocaleCount16 = static_cast<sal_Int16>(nLocaleCount);
+ sal_Int16 iDefault16 = static_cast<sal_Int16>(iDefault);
+ aOut.writeInt16( 0 ); // nVersion
+ aOut.writeInt16( nLocaleCount16 );
+ aOut.writeInt16( iDefault16 );
+
+ // Write data positions
+ sal_Int32 nDataPos = 6 + 4 * (nLocaleCount + 1);
+ for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
+ {
+ aOut.writeInt32( nDataPos );
+
+ Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
+ sal_Int32 nSeqLen = rSeq.getLength();
+ nDataPos += nSeqLen;
+ }
+ // Write final position
+ aOut.writeInt32( nDataPos );
+
+ // Write data
+ Reference< io::XOutputStream > xOutputStream = aOut.getOutputStream();
+ if( xOutputStream.is() )
+ {
+ for( iLocale = 0; iLocale < nLocaleCount; iLocale++ )
+ {
+ Sequence< sal_Int8 >& rSeq = aLocaleDataSeq[iLocale];
+ xOutputStream->writeBytes( rSeq );
+ }
+ }
+
+ Sequence< sal_Int8 > aRetSeq = aOut.closeAndGetData();
+ return aRetSeq;
+}
+
+void StringResourcePersistenceImpl::implWriteLocaleBinary
+ ( LocaleItem* pLocaleItem, BinaryOutput& rOut )
+{
+ Reference< io::XOutputStream > xOutputStream = rOut.getOutputStream();
+ if( !xOutputStream.is() )
+ return;
+
+ Locale& rLocale = pLocaleItem->m_locale;
+ rOut.writeString( rLocale.Language );
+ rOut.writeString( rLocale.Country );
+ rOut.writeString( rLocale.Variant );
+ implWritePropertiesFile( pLocaleItem, xOutputStream, m_aComment );
+}
+
+
+// BinaryOutput, helper class for exportBinary
+
+namespace {
+
+class BinaryInput
+{
+ Sequence< sal_Int8 > m_aData;
+
+ const sal_Int8* m_pData;
+ sal_Int32 m_nCurPos;
+ sal_Int32 m_nSize;
+
+public:
+ BinaryInput( const Sequence< ::sal_Int8 >& aData );
+
+ Reference< io::XInputStream > getInputStreamForSection( sal_Int32 nSize );
+
+ void seek( sal_Int32 nPos );
+ sal_Int32 getPosition() const
+ { return m_nCurPos; }
+
+ sal_Int16 readInt16();
+ sal_Int32 readInt32();
+ sal_Unicode readUnicodeChar();
+ OUString readString();
+};
+
+}
+
+BinaryInput::BinaryInput( const Sequence< ::sal_Int8 >& aData )
+ : m_aData( aData )
+{
+ m_pData = m_aData.getConstArray();
+ m_nCurPos = 0;
+ m_nSize = m_aData.getLength();
+}
+
+Reference< io::XInputStream > BinaryInput::getInputStreamForSection( sal_Int32 nSize )
+{
+ Reference< io::XInputStream > xIn;
+ if( m_nCurPos + nSize <= m_nSize )
+ {
+ rtl::Reference< utl::TempFileFastService > xTempOut = new utl::TempFileFastService;
+ Sequence< sal_Int8 > aSection( m_pData + m_nCurPos, nSize );
+ xTempOut->writeBytes( aSection );
+ xTempOut->seek( 0 );
+ xIn = xTempOut;
+ }
+ else
+ OSL_FAIL( "BinaryInput::getInputStreamForSection(): Read past end" );
+
+ return xIn;
+}
+
+void BinaryInput::seek( sal_Int32 nPos )
+{
+ if( nPos <= m_nSize )
+ m_nCurPos = nPos;
+ else
+ OSL_FAIL( "BinaryInput::seek(): Position past end" );
+}
+
+
+sal_Int16 BinaryInput::readInt16()
+{
+ sal_Int16 nRet = 0;
+ if( m_nCurPos + 2 <= m_nSize )
+ {
+ nRet = nRet + sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
+ nRet += 256 * sal_Int16( sal_uInt8( m_pData[m_nCurPos++] ) );
+ }
+ else
+ OSL_FAIL( "BinaryInput::readInt16(): Read past end" );
+
+ return nRet;
+}
+
+sal_Int32 BinaryInput::readInt32()
+{
+ sal_Int32 nRet = 0;
+ if( m_nCurPos + 4 <= m_nSize )
+ {
+ sal_Int32 nFactor = 1;
+ for( sal_Int16 i = 0; i < 4; i++ )
+ {
+ nRet += sal_uInt8( m_pData[m_nCurPos++] ) * nFactor;
+ nFactor *= 256;
+ }
+ }
+ else
+ OSL_FAIL( "BinaryInput::readInt32(): Read past end" );
+
+ return nRet;
+}
+
+sal_Unicode BinaryInput::readUnicodeChar()
+{
+ sal_uInt16 nRet = 0;
+ if( m_nCurPos + 2 <= m_nSize )
+ {
+ nRet = nRet + sal_uInt8( m_pData[m_nCurPos++] );
+ nRet += 256 * sal_uInt8( m_pData[m_nCurPos++] );
+ }
+ else
+ OSL_FAIL( "BinaryInput::readUnicodeChar(): Read past end" );
+
+ sal_Unicode cRet = nRet;
+ return cRet;
+}
+
+OUString BinaryInput::readString()
+{
+ OUStringBuffer aBuf;
+ sal_Unicode c;
+ do
+ {
+ c = readUnicodeChar();
+ if( c != 0 )
+ aBuf.append( c );
+ }
+ while( c != 0 );
+
+ OUString aRetStr = aBuf.makeStringAndClear();
+ return aRetStr;
+}
+
+void StringResourcePersistenceImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
+{
+ // Init: Remove all locales
+ sal_Int32 nOldLocaleCount = 0;
+ do
+ {
+ Sequence< Locale > aLocaleSeq = getLocales();
+ nOldLocaleCount = aLocaleSeq.getLength();
+ if( nOldLocaleCount > 0 )
+ {
+ Locale aLocale = aLocaleSeq[0];
+ removeLocale( aLocale );
+ }
+ }
+ while( nOldLocaleCount > 0 );
+
+ // Import data
+ BinaryInput aIn( Data );
+
+ aIn.readInt16(); // version
+ sal_Int32 nLocaleCount = aIn.readInt16();
+ sal_Int32 iDefault = aIn.readInt16();
+
+ std::unique_ptr<sal_Int32[]> pPositions( new sal_Int32[nLocaleCount + 1] );
+ for( sal_Int32 i = 0; i < nLocaleCount + 1; i++ )
+ pPositions[i] = aIn.readInt32();
+
+ // Import locales
+ LocaleItem* pUseAsDefaultItem = nullptr;
+ for( sal_Int32 i = 0; i < nLocaleCount; i++ )
+ {
+ sal_Int32 nPos = pPositions[i];
+ aIn.seek( nPos );
+
+ Locale aLocale;
+ aLocale.Language = aIn.readString();
+ aLocale.Country = aIn.readString();
+ aLocale.Variant = aIn.readString();
+
+ sal_Int32 nAfterStringPos = aIn.getPosition();
+ sal_Int32 nSize = pPositions[i+1] - nAfterStringPos;
+ Reference< io::XInputStream > xInput = aIn.getInputStreamForSection( nSize );
+ if( xInput.is() )
+ {
+ LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale) );
+ if( iDefault == i )
+ pUseAsDefaultItem = pLocaleItem;
+ m_aLocaleItemVector.emplace_back( pLocaleItem );
+ implReadPropertiesFile( pLocaleItem, xInput );
+ }
+ }
+
+ if( pUseAsDefaultItem != nullptr )
+ setDefaultLocale( pUseAsDefaultItem->m_locale );
+}
+
+
+// Private helper methods
+
+static bool checkNamingSceme( std::u16string_view aName, std::u16string_view aNameBase,
+ Locale& aLocale )
+{
+ bool bSuccess = false;
+
+ size_t nNameLen = aName.size();
+ size_t nNameBaseLen = aNameBase.size();
+
+ // Name has to start with NameBase followed
+ // by a '_' and at least one more character
+ if( o3tl::starts_with(aName, aNameBase) && nNameBaseLen < nNameLen-1 &&
+ aName[nNameBaseLen] == '_' )
+ {
+ bSuccess = true;
+
+ /* FIXME-BCP47: this uses '_' underscore character as separator and
+ * also appends Variant, which can't be blindly changed as it would
+ * violate the naming scheme in use. */
+
+ sal_Int32 iStart = nNameBaseLen + 1;
+ size_t iNext_ = aName.find( '_', iStart );
+ if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
+ {
+ aLocale.Language = aName.substr( iStart, iNext_ - iStart );
+
+ iStart = iNext_ + 1;
+ iNext_ = aName.find( '_', iStart );
+ if( iNext_ != std::u16string_view::npos && iNext_ < nNameLen-1 )
+ {
+ aLocale.Country = aName.substr( iStart, iNext_ - iStart );
+ aLocale.Variant = aName.substr( iNext_ + 1 );
+ }
+ else
+ aLocale.Country = aName.substr( iStart );
+ }
+ else
+ aLocale.Language = aName.substr( iStart );
+ }
+ return bSuccess;
+}
+
+void StringResourcePersistenceImpl::implLoadAllLocales()
+{
+ for( auto& pLocaleItem : m_aLocaleItemVector )
+ if( pLocaleItem )
+ loadLocale( pLocaleItem.get() );
+}
+
+// Scan locale properties files helper
+void StringResourcePersistenceImpl::implScanLocaleNames( const Sequence< OUString >& aContentSeq )
+{
+ Locale aDefaultLocale;
+ bool bDefaultFound = false;
+
+ for( const OUString& aCompleteName : aContentSeq )
+ {
+ OUString aPureName;
+ OUString aExtension;
+ sal_Int32 iDot = aCompleteName.lastIndexOf( '.' );
+ sal_Int32 iSlash = aCompleteName.lastIndexOf( '/' );
+ if( iDot != -1 && iDot > iSlash)
+ {
+ sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0;
+ aPureName = aCompleteName.copy( iCopyFrom, iDot-iCopyFrom );
+ aExtension = aCompleteName.copy( iDot + 1 );
+ }
+
+ if ( aExtension == "properties" )
+ {
+ //OUString aName = aInetObj.getBase();
+ Locale aLocale;
+
+ if( checkNamingSceme( aPureName, m_aNameBase, aLocale ) )
+ {
+ LocaleItem* pLocaleItem = new LocaleItem( std::move(aLocale), false );
+ m_aLocaleItemVector.emplace_back( pLocaleItem );
+
+ if( m_pCurrentLocaleItem == nullptr )
+ m_pCurrentLocaleItem = pLocaleItem;
+
+ if( m_pDefaultLocaleItem == nullptr )
+ {
+ m_pDefaultLocaleItem = pLocaleItem;
+ m_bDefaultModified = true;
+ }
+ }
+ }
+ else if( !bDefaultFound && aExtension == "default" )
+ {
+ if( checkNamingSceme( aPureName, m_aNameBase, aDefaultLocale ) )
+ bDefaultFound = true;
+ }
+ }
+ if( bDefaultFound )
+ {
+ LocaleItem* pLocaleItem = getItemForLocale( aDefaultLocale, false );
+ if( pLocaleItem )
+ {
+ m_pDefaultLocaleItem = pLocaleItem;
+ m_bDefaultModified = false;
+ }
+ }
+}
+
+// Scan locale properties files
+void StringResourcePersistenceImpl::implScanLocales()
+{
+ // Dummy implementation, method not called for this
+ // base class, but pure virtual not possible-
+}
+
+bool StringResourcePersistenceImpl::loadLocale( LocaleItem* pLocaleItem )
+{
+ bool bSuccess = false;
+
+ OSL_ENSURE( pLocaleItem, "StringResourcePersistenceImpl::loadLocale(): pLocaleItem == NULL" );
+ if( pLocaleItem )
+ {
+ if( pLocaleItem->m_bLoaded )
+ {
+ bSuccess = true;
+ }
+ else
+ {
+ bSuccess = implLoadLocale( pLocaleItem );
+ pLocaleItem->m_bLoaded = true; // = bSuccess??? -> leads to more tries
+ }
+ }
+ return bSuccess;
+}
+
+bool StringResourcePersistenceImpl::implLoadLocale( LocaleItem* )
+{
+ // Dummy implementation, method not called for this
+ // base class, but pure virtual not possible-
+ return false;
+}
+
+static OUString implGetNameScemeForLocaleItem( const LocaleItem* pLocaleItem )
+{
+ /* FIXME-BCP47: this uses '_' underscore character as separator and
+ * also appends Variant, which can't be blindly changed as it would
+ * violate the naming scheme in use. */
+
+ static const char aUnder[] = "_";
+
+ OSL_ENSURE( pLocaleItem,
+ "StringResourcePersistenceImpl::implGetNameScemeForLocaleItem(): pLocaleItem == NULL" );
+ Locale aLocale = pLocaleItem->m_locale;
+
+ OUString aRetStr = aUnder + aLocale.Language;
+
+ OUString aCountry = aLocale.Country;
+ if( !aCountry.isEmpty() )
+ {
+ aRetStr += aUnder + aCountry;
+ }
+
+ OUString aVariant = aLocale.Variant;
+ if( !aVariant.isEmpty() )
+ {
+ aRetStr += aUnder + aVariant;
+ }
+ return aRetStr;
+}
+
+OUString StringResourcePersistenceImpl::implGetFileNameForLocaleItem
+ ( LocaleItem const * pLocaleItem, const OUString& aNameBase )
+{
+ OUString aFileName = aNameBase;
+ if( aFileName.isEmpty() )
+ aFileName = aNameBaseDefaultStr;
+
+ aFileName += implGetNameScemeForLocaleItem( pLocaleItem );
+ return aFileName;
+}
+
+OUString StringResourcePersistenceImpl::implGetPathForLocaleItem
+ ( LocaleItem const * pLocaleItem, const OUString& aNameBase,
+ std::u16string_view aLocation, bool bDefaultFile )
+{
+ OUString aFileName = implGetFileNameForLocaleItem( pLocaleItem, aNameBase );
+ INetURLObject aInetObj( aLocation );
+ aInetObj.insertName( aFileName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All );
+ if( bDefaultFile )
+ aInetObj.setExtension( u"default" );
+ else
+ aInetObj.setExtension( u"properties" );
+ OUString aCompleteFileName = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ return aCompleteFileName;
+}
+
+// White space according to Java property files specification in
+// http://java.sun.com/j2se/1.4.2/docs/api/java/util/Properties.html#load(java.io.InputStream)
+static bool isWhiteSpace( sal_Unicode c )
+{
+ bool bWhite = ( c == 0x0020 || // space
+ c == 0x0009 || // tab
+ c == 0x000a || // line feed, not always handled by TextInputStream
+ c == 0x000d || // carriage return, not always handled by TextInputStream
+ c == 0x000C ); // form feed
+ return bWhite;
+}
+
+static void skipWhites( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
+{
+ while( ri < nLen )
+ {
+ if( !isWhiteSpace( pBuf[ri] ) )
+ break;
+ ri++;
+ }
+}
+
+static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
+{
+ bool bRet = true;
+ if( c >= '0' && c <= '9' )
+ nDigitVal = c - '0';
+ else if( c >= 'a' && c <= 'f' )
+ nDigitVal = c - 'a' + 10;
+ else if( c >= 'A' && c <= 'F' )
+ nDigitVal = c - 'A' + 10;
+ else
+ bRet = false;
+ return bRet;
+}
+
+static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, sal_Int32& ri )
+{
+ sal_Int32 i = ri;
+
+ sal_Unicode cRet = 0;
+ sal_Unicode c = pBuf[i];
+ switch( c )
+ {
+ case 't':
+ cRet = 0x0009;
+ break;
+ case 'n':
+ cRet = 0x000a;
+ break;
+ case 'f':
+ cRet = 0x000c;
+ break;
+ case 'r':
+ cRet = 0x000d;
+ break;
+ case '\\':
+ cRet = '\\';
+ break;
+ case 'u':
+ {
+ // Skip multiple u
+ i++;
+ while( i < nLen && pBuf[i] == 'u' )
+ i++;
+
+ // Process hex digits
+ sal_Int32 nDigitCount = 0;
+ sal_uInt16 nDigitVal;
+ while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
+ {
+ cRet = 16 * cRet + nDigitVal;
+
+ nDigitCount++;
+ if( nDigitCount == 4 )
+ {
+ // Write back position
+ ri = i;
+ break;
+ }
+ i++;
+ }
+ break;
+ }
+ default:
+ cRet = c;
+ }
+
+ return cRet;
+}
+
+static void CheckContinueInNextLine( const Reference< io::XTextInputStream2 >& xTextInputStream,
+ OUString& aLine, bool& bEscapePending, const sal_Unicode*& pBuf,
+ sal_Int32& nLen, sal_Int32& i )
+{
+ if( !(i == nLen && bEscapePending) )
+ return;
+
+ bEscapePending = false;
+
+ if( !xTextInputStream->isEOF() )
+ {
+ aLine = xTextInputStream->readLine();
+ nLen = aLine.getLength();
+ pBuf = aLine.getStr();
+ i = 0;
+
+ skipWhites( pBuf, nLen, i );
+ }
+}
+
+bool StringResourcePersistenceImpl::implReadPropertiesFile
+ ( LocaleItem* pLocaleItem, const Reference< io::XInputStream >& xInputStream )
+{
+ if( !xInputStream.is() || pLocaleItem == nullptr )
+ return false;
+
+ Reference< io::XTextInputStream2 > xTextInputStream = io::TextInputStream::create( m_xContext );
+
+ xTextInputStream->setInputStream( xInputStream );
+
+ OUString aEncodingStr = OUString::createFromAscii
+ ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
+ xTextInputStream->setEncoding( aEncodingStr );
+
+ OUString aLine;
+ while( !xTextInputStream->isEOF() )
+ {
+ aLine = xTextInputStream->readLine();
+
+ sal_Int32 nLen = aLine.getLength();
+ if( 0 == nLen )
+ continue;
+ const sal_Unicode* pBuf = aLine.getStr();
+ OUStringBuffer aBuf;
+ sal_Unicode c = 0;
+ sal_Int32 i = 0;
+
+ skipWhites( pBuf, nLen, i );
+ if( i == nLen )
+ continue; // line contains only white spaces
+
+ // Comment?
+ c = pBuf[i];
+ if( c == '#' || c == '!' )
+ continue;
+
+ // Scan key
+ OUString aResourceID;
+ bool bEscapePending = false;
+ bool bStrComplete = false;
+ while( i < nLen && !bStrComplete )
+ {
+ c = pBuf[i];
+ if( bEscapePending )
+ {
+ aBuf.append( getEscapeChar( pBuf, nLen, i ) );
+ bEscapePending = false;
+ }
+ else
+ {
+ if( c == '\\' )
+ {
+ bEscapePending = true;
+ }
+ else
+ {
+ if( c == ':' || c == '=' || isWhiteSpace( c ) )
+ bStrComplete = true;
+ else
+ aBuf.append( c );
+ }
+ }
+ i++;
+
+ CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
+ if( i == nLen )
+ bStrComplete = true;
+
+ if( bStrComplete )
+ aResourceID = aBuf.makeStringAndClear();
+ }
+
+ // Ignore lines with empty keys
+ if( aResourceID.isEmpty() )
+ continue;
+
+ // Scan value
+ skipWhites( pBuf, nLen, i );
+
+ OUString aValueStr;
+ bEscapePending = false;
+ bStrComplete = false;
+ while( i < nLen && !bStrComplete )
+ {
+ c = pBuf[i];
+ if( c == 0x000a || c == 0x000d ) // line feed/carriage return, not always handled by TextInputStream
+ {
+ i++;
+ }
+ else
+ {
+ if( bEscapePending )
+ {
+ aBuf.append( getEscapeChar( pBuf, nLen, i ) );
+ bEscapePending = false;
+ }
+ else if( c == '\\' )
+ bEscapePending = true;
+ else
+ aBuf.append( c );
+ i++;
+
+ CheckContinueInNextLine( xTextInputStream, aLine, bEscapePending, pBuf, nLen, i );
+ }
+ if( i == nLen )
+ bStrComplete = true;
+
+ if( bStrComplete )
+ aValueStr = aBuf.makeStringAndClear();
+ }
+
+ // Push into table
+ pLocaleItem->m_aIdToStringMap[ aResourceID ] = aValueStr;
+ implScanIdForNumber( aResourceID );
+ IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
+ rIndexMap[ aResourceID ] = pLocaleItem->m_nNextIndex++;
+ }
+
+ return true;
+}
+
+
+static sal_Unicode getHexCharForDigit( sal_uInt16 nDigitVal )
+{
+ sal_Unicode cRet = ( nDigitVal < 10 ) ? ('0' + nDigitVal) : ('a' + (nDigitVal-10));
+ return cRet;
+}
+
+static void implWriteCharToBuffer( OUStringBuffer& aBuf, sal_Unicode cu, bool bKey )
+{
+ if( cu == '\\' )
+ {
+ aBuf.append( '\\' );
+ aBuf.append( '\\' );
+ }
+ else if( cu == 0x000a )
+ {
+ aBuf.append( '\\' );
+ aBuf.append( 'n' );
+ }
+ else if( cu == 0x000d )
+ {
+ aBuf.append( '\\' );
+ aBuf.append( 'r' );
+ }
+ else if( bKey && cu == '=' )
+ {
+ aBuf.append( '\\' );
+ aBuf.append( '=' );
+ }
+ else if( bKey && cu == ':' )
+ {
+ aBuf.append( '\\' );
+ aBuf.append( ':' );
+ }
+ // ISO/IEC 8859-1 range according to:
+ // http://en.wikipedia.org/wiki/ISO/IEC_8859-1
+ else if( cu >= 0x20 && cu <= 0x7e )
+ //TODO: Check why (cu >= 0xa0 && cu <= 0xFF)
+ //is encoded in sample properties files
+ //else if( (cu >= 0x20 && cu <= 0x7e) ||
+ // (cu >= 0xa0 && cu <= 0xFF) )
+ {
+ aBuf.append( cu );
+ }
+ else
+ {
+ // Unicode encoding
+ aBuf.append( '\\' );
+ aBuf.append( 'u' );
+
+ sal_uInt16 nVal = cu;
+ for( sal_uInt16 i = 0 ; i < 4 ; i++ )
+ {
+ sal_uInt16 nDigit = nVal / 0x1000;
+ nVal -= nDigit * 0x1000;
+ nVal *= 0x10;
+ aBuf.append( getHexCharForDigit( nDigit ) );
+ }
+ }
+}
+
+static void implWriteStringWithEncoding( const OUString& aStr,
+ Reference< io::XTextOutputStream2 > const & xTextOutputStream, bool bKey )
+{
+ static const sal_Unicode cLineFeed = 0xa;
+
+ OUStringBuffer aBuf;
+ sal_Int32 nLen = aStr.getLength();
+ const sal_Unicode* pSrc = aStr.getStr();
+ for( sal_Int32 i = 0 ; i < nLen ; i++ )
+ {
+ sal_Unicode cu = pSrc[i];
+ implWriteCharToBuffer( aBuf, cu, bKey );
+ // TODO?: split long lines
+ }
+ if( !bKey )
+ aBuf.append( cLineFeed );
+
+ OUString aWriteStr = aBuf.makeStringAndClear();
+ xTextOutputStream->writeString( aWriteStr );
+}
+
+bool StringResourcePersistenceImpl::implWritePropertiesFile( LocaleItem const * pLocaleItem,
+ const Reference< io::XOutputStream >& xOutputStream, const OUString& aComment )
+{
+ if( !xOutputStream.is() || pLocaleItem == nullptr )
+ return false;
+
+ bool bSuccess = false;
+ Reference< io::XTextOutputStream2 > xTextOutputStream = io::TextOutputStream::create(m_xContext);
+
+ xTextOutputStream->setOutputStream( xOutputStream );
+
+ OUString aEncodingStr = OUString::createFromAscii
+ ( rtl_getMimeCharsetFromTextEncoding( RTL_TEXTENCODING_ISO_8859_1 ) );
+ xTextOutputStream->setEncoding( aEncodingStr );
+
+ xTextOutputStream->writeString( aComment );
+ xTextOutputStream->writeString( "\n" );
+
+ const IdToStringMap& rHashMap = pLocaleItem->m_aIdToStringMap;
+ if( !rHashMap.empty() )
+ {
+ // Sort ids according to read order
+ const IdToIndexMap& rIndexMap = pLocaleItem->m_aIdToIndexMap;
+
+ // Find max/min index
+ auto itMinMax = std::minmax_element(rIndexMap.begin(), rIndexMap.end(),
+ [](const IdToIndexMap::value_type& a, const IdToIndexMap::value_type& b) { return a.second < b.second; });
+ sal_Int32 nMinIndex = itMinMax.first->second;
+ sal_Int32 nMaxIndex = itMinMax.second->second;
+ sal_Int32 nTabSize = nMaxIndex - nMinIndex + 1;
+
+ // Create sorted array of pointers to the id strings
+ std::unique_ptr<const OUString*[]> pIdPtrs( new const OUString*[nTabSize] );
+ for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
+ pIdPtrs[i] = nullptr;
+ for( const auto& rIndex : rIndexMap )
+ {
+ sal_Int32 nIndex = rIndex.second;
+ pIdPtrs[nIndex - nMinIndex] = &(rIndex.first);
+ }
+
+ // Write lines in correct order
+ for(sal_Int32 i = 0 ; i < nTabSize ; i++ )
+ {
+ const OUString* pStr = pIdPtrs[i];
+ if( pStr != nullptr )
+ {
+ OUString aResourceID = *pStr;
+ IdToStringMap::const_iterator it = rHashMap.find( aResourceID );
+ if( it != rHashMap.end() )
+ {
+ implWriteStringWithEncoding( aResourceID, xTextOutputStream, true );
+ xTextOutputStream->writeString( "=" );
+ OUString aValStr = (*it).second;
+ implWriteStringWithEncoding( aValStr, xTextOutputStream, false );
+ }
+ }
+ }
+ }
+
+ bSuccess = true;
+
+ return bSuccess;
+}
+
+
+// StringResourceWithStorageImpl
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_StringResourceWithStorageImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new StringResourceWithStorageImpl(context));
+}
+
+
+StringResourceWithStorageImpl::StringResourceWithStorageImpl( const Reference< XComponentContext >& rxContext )
+ : StringResourceWithStorageImpl_BASE( rxContext )
+ , m_bStorageChanged( false )
+{
+}
+
+
+StringResourceWithStorageImpl::~StringResourceWithStorageImpl()
+{
+}
+
+
+// XServiceInfo
+
+
+OUString StringResourceWithStorageImpl::getImplementationName( )
+{
+ return "com.sun.star.comp.scripting.StringResourceWithStorage";
+}
+
+sal_Bool StringResourceWithStorageImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > StringResourceWithStorageImpl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.resource.StringResourceWithStorage" };
+}
+
+
+// XInitialization
+
+
+void StringResourceWithStorageImpl::initialize( const Sequence< Any >& aArguments )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( aArguments.getLength() != 5 )
+ {
+ throw RuntimeException(
+ "StringResourceWithStorageImpl::initialize: invalid number of arguments!" );
+ }
+
+ bool bOk = (aArguments[0] >>= m_xStorage);
+ if( bOk && !m_xStorage.is() )
+ bOk = false;
+
+ if( !bOk )
+ {
+ throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid storage", Reference< XInterface >(), 0 );
+ }
+
+ implInitializeCommonParameters( aGuard, aArguments );
+}
+
+
+// Forwarding calls to base class
+
+// XModifyBroadcaster
+void StringResourceWithStorageImpl::addModifyListener( const Reference< XModifyListener >& aListener )
+{
+ StringResourceImpl::addModifyListener( aListener );
+}
+void StringResourceWithStorageImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
+{
+ StringResourceImpl::removeModifyListener( aListener );
+}
+
+// XStringResourceResolver
+OUString StringResourceWithStorageImpl::resolveString( const OUString& ResourceID )
+{
+ return StringResourceImpl::resolveString( ResourceID ) ;
+}
+OUString StringResourceWithStorageImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
+}
+sal_Bool StringResourceWithStorageImpl::hasEntryForId( const OUString& ResourceID )
+{
+ return StringResourceImpl::hasEntryForId( ResourceID ) ;
+}
+sal_Bool StringResourceWithStorageImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
+ const Locale& locale )
+{
+ return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
+}
+Sequence< OUString > StringResourceWithStorageImpl::getResourceIDs( )
+{
+ return StringResourceImpl::getResourceIDs();
+}
+Sequence< OUString > StringResourceWithStorageImpl::getResourceIDsForLocale
+ ( const Locale& locale )
+{
+ return StringResourceImpl::getResourceIDsForLocale( locale );
+}
+Locale StringResourceWithStorageImpl::getCurrentLocale()
+{
+ return StringResourceImpl::getCurrentLocale();
+}
+Locale StringResourceWithStorageImpl::getDefaultLocale( )
+{
+ return StringResourceImpl::getDefaultLocale();
+}
+Sequence< Locale > StringResourceWithStorageImpl::getLocales( )
+{
+ return StringResourceImpl::getLocales();
+}
+
+// XStringResourceManager
+sal_Bool StringResourceWithStorageImpl::isReadOnly()
+{
+ return StringResourceImpl::isReadOnly();
+}
+void StringResourceWithStorageImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
+{
+ StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
+}
+void StringResourceWithStorageImpl::setDefaultLocale( const Locale& locale )
+{
+ StringResourceImpl::setDefaultLocale( locale );
+}
+void StringResourceWithStorageImpl::setString( const OUString& ResourceID, const OUString& Str )
+{
+ StringResourceImpl::setString( ResourceID, Str );
+}
+void StringResourceWithStorageImpl::setStringForLocale
+ ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
+{
+ StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
+}
+void StringResourceWithStorageImpl::removeId( const OUString& ResourceID )
+{
+ StringResourceImpl::removeId( ResourceID );
+}
+void StringResourceWithStorageImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ StringResourceImpl::removeIdForLocale( ResourceID, locale );
+}
+void StringResourceWithStorageImpl::newLocale( const Locale& locale )
+{
+ StringResourceImpl::newLocale( locale );
+}
+void StringResourceWithStorageImpl::removeLocale( const Locale& locale )
+{
+ StringResourceImpl::removeLocale( locale );
+}
+sal_Int32 StringResourceWithStorageImpl::getUniqueNumericId( )
+{
+ return StringResourceImpl::getUniqueNumericId();
+}
+
+// XStringResourcePersistence
+void StringResourceWithStorageImpl::store()
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceWithStorageImpl::store(): Read only" );
+
+ bool bStoreAll = m_bStorageChanged;
+ m_bStorageChanged = false;
+ if( !m_bModified && !bStoreAll )
+ return;
+
+ implStoreAtStorage( m_aNameBase, m_aComment, m_xStorage, true/*bUsedForStore*/, bStoreAll );
+ m_bModified = false;
+}
+
+sal_Bool StringResourceWithStorageImpl::isModified( )
+{
+ return StringResourcePersistenceImpl::isModified();
+}
+void StringResourceWithStorageImpl::setComment( const OUString& Comment )
+{
+ StringResourcePersistenceImpl::setComment( Comment );
+}
+void StringResourceWithStorageImpl::storeToStorage( const Reference< XStorage >& Storage,
+ const OUString& NameBase, const OUString& Comment )
+{
+ StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
+}
+void StringResourceWithStorageImpl::storeToURL( const OUString& URL,
+ const OUString& NameBase, const OUString& Comment,
+ const Reference< css::task::XInteractionHandler >& Handler )
+{
+ StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
+}
+Sequence< ::sal_Int8 > StringResourceWithStorageImpl::exportBinary( )
+{
+ return StringResourcePersistenceImpl::exportBinary();
+}
+void StringResourceWithStorageImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
+{
+ StringResourcePersistenceImpl::importBinary( Data );
+}
+
+
+// XStringResourceWithStorage
+
+void StringResourceWithStorageImpl::storeAsStorage( const Reference< XStorage >& Storage )
+{
+ setStorage( Storage );
+ store();
+}
+
+void StringResourceWithStorageImpl::setStorage( const Reference< XStorage >& Storage )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if( !Storage.is() )
+ {
+ throw IllegalArgumentException( "StringResourceWithStorageImpl::setStorage: invalid storage", Reference< XInterface >(), 0 );
+ }
+
+ implLoadAllLocales();
+
+ m_xStorage = Storage;
+ m_bStorageChanged = true;
+}
+
+
+// Private helper methods
+
+
+// Scan locale properties files
+void StringResourceWithStorageImpl::implScanLocales()
+{
+ if( m_xStorage.is() )
+ {
+ Sequence< OUString > aContentSeq = m_xStorage->getElementNames();
+ implScanLocaleNames( aContentSeq );
+ }
+
+ implLoadAllLocales();
+}
+
+// Loading
+bool StringResourceWithStorageImpl::implLoadLocale( LocaleItem* pLocaleItem )
+{
+ bool bSuccess = false;
+ try
+ {
+ OUString aStreamName = implGetFileNameForLocaleItem( pLocaleItem, m_aNameBase ) + ".properties";
+
+ Reference< io::XStream > xElementStream =
+ m_xStorage->openStreamElement( aStreamName, ElementModes::READ );
+
+ if( xElementStream.is() )
+ {
+ Reference< io::XInputStream > xInputStream = xElementStream->getInputStream();
+ if( xInputStream.is() )
+ {
+ bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
+ xInputStream->closeInput();
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {}
+
+ return bSuccess;
+}
+
+
+// StringResourceWithLocationImpl
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_StringResourceWithLocationImpl_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new StringResourceWithLocationImpl(context));
+}
+
+
+
+StringResourceWithLocationImpl::StringResourceWithLocationImpl( const Reference< XComponentContext >& rxContext )
+ : StringResourceWithLocationImpl_BASE( rxContext )
+ , m_bLocationChanged( false )
+{
+}
+
+
+StringResourceWithLocationImpl::~StringResourceWithLocationImpl()
+{
+}
+
+
+// XServiceInfo
+
+
+OUString StringResourceWithLocationImpl::getImplementationName( )
+{
+ return "com.sun.star.comp.scripting.StringResourceWithLocation";
+}
+
+sal_Bool StringResourceWithLocationImpl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > StringResourceWithLocationImpl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.resource.StringResourceWithLocation" };
+}
+
+
+// XInitialization
+
+
+void StringResourceWithLocationImpl::initialize( const Sequence< Any >& aArguments )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( aArguments.getLength() != 6 )
+ {
+ throw RuntimeException(
+ "XInitialization::initialize: invalid number of arguments!" );
+ }
+
+ bool bOk = (aArguments[0] >>= m_aLocation);
+ sal_Int32 nLen = m_aLocation.getLength();
+ if( bOk && nLen == 0 )
+ {
+ bOk = false;
+ }
+ else
+ {
+ if( m_aLocation[nLen - 1] != '/' )
+ m_aLocation += "/";
+ }
+
+ if( !bOk )
+ {
+ throw IllegalArgumentException( "XInitialization::initialize: invalid URL", Reference< XInterface >(), 0 );
+ }
+
+
+ bOk = (aArguments[5] >>= m_xInteractionHandler);
+ if( !bOk )
+ {
+ throw IllegalArgumentException( "StringResourceWithStorageImpl::initialize: invalid type", Reference< XInterface >(), 5 );
+ }
+
+ implInitializeCommonParameters( aGuard, aArguments );
+}
+
+
+// Forwarding calls to base class
+
+// XModifyBroadcaster
+void StringResourceWithLocationImpl::addModifyListener( const Reference< XModifyListener >& aListener )
+{
+ StringResourceImpl::addModifyListener( aListener );
+}
+void StringResourceWithLocationImpl::removeModifyListener( const Reference< XModifyListener >& aListener )
+{
+ StringResourceImpl::removeModifyListener( aListener );
+}
+
+// XStringResourceResolver
+OUString StringResourceWithLocationImpl::resolveString( const OUString& ResourceID )
+{
+ return StringResourceImpl::resolveString( ResourceID ) ;
+}
+OUString StringResourceWithLocationImpl::resolveStringForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ return StringResourceImpl::resolveStringForLocale( ResourceID, locale );
+}
+sal_Bool StringResourceWithLocationImpl::hasEntryForId( const OUString& ResourceID )
+{
+ return StringResourceImpl::hasEntryForId( ResourceID ) ;
+}
+sal_Bool StringResourceWithLocationImpl::hasEntryForIdAndLocale( const OUString& ResourceID,
+ const Locale& locale )
+{
+ return StringResourceImpl::hasEntryForIdAndLocale( ResourceID, locale );
+}
+Sequence< OUString > StringResourceWithLocationImpl::getResourceIDs( )
+{
+ return StringResourceImpl::getResourceIDs();
+}
+Sequence< OUString > StringResourceWithLocationImpl::getResourceIDsForLocale
+ ( const Locale& locale )
+{
+ return StringResourceImpl::getResourceIDsForLocale( locale );
+}
+Locale StringResourceWithLocationImpl::getCurrentLocale()
+{
+ return StringResourceImpl::getCurrentLocale();
+}
+Locale StringResourceWithLocationImpl::getDefaultLocale( )
+{
+ return StringResourceImpl::getDefaultLocale();
+}
+Sequence< Locale > StringResourceWithLocationImpl::getLocales( )
+{
+ return StringResourceImpl::getLocales();
+}
+
+// XStringResourceManager
+sal_Bool StringResourceWithLocationImpl::isReadOnly()
+{
+ return StringResourceImpl::isReadOnly();
+}
+void StringResourceWithLocationImpl::setCurrentLocale( const Locale& locale, sal_Bool FindClosestMatch )
+{
+ StringResourceImpl::setCurrentLocale( locale, FindClosestMatch );
+}
+void StringResourceWithLocationImpl::setDefaultLocale( const Locale& locale )
+{
+ StringResourceImpl::setDefaultLocale( locale );
+}
+void StringResourceWithLocationImpl::setString( const OUString& ResourceID, const OUString& Str )
+{
+ StringResourceImpl::setString( ResourceID, Str );
+}
+void StringResourceWithLocationImpl::setStringForLocale
+ ( const OUString& ResourceID, const OUString& Str, const Locale& locale )
+{
+ StringResourceImpl::setStringForLocale( ResourceID, Str, locale );
+}
+void StringResourceWithLocationImpl::removeId( const OUString& ResourceID )
+{
+ StringResourceImpl::removeId( ResourceID );
+}
+void StringResourceWithLocationImpl::removeIdForLocale( const OUString& ResourceID, const Locale& locale )
+{
+ StringResourceImpl::removeIdForLocale( ResourceID, locale );
+}
+void StringResourceWithLocationImpl::newLocale( const Locale& locale )
+{
+ StringResourceImpl::newLocale( locale );
+}
+void StringResourceWithLocationImpl::removeLocale( const Locale& locale )
+{
+ StringResourceImpl::removeLocale( locale );
+}
+sal_Int32 StringResourceWithLocationImpl::getUniqueNumericId( )
+{
+ return StringResourceImpl::getUniqueNumericId();
+}
+
+// XStringResourcePersistence
+void StringResourceWithLocationImpl::store()
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceWithLocationImpl::store(): Read only" );
+
+ bool bStoreAll = m_bLocationChanged;
+ m_bLocationChanged = false;
+ if( !m_bModified && !bStoreAll )
+ return;
+
+ Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
+ implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
+ xFileAccess, true/*bUsedForStore*/, bStoreAll );
+ m_bModified = false;
+}
+
+sal_Bool StringResourceWithLocationImpl::isModified( )
+{
+ return StringResourcePersistenceImpl::isModified();
+}
+void StringResourceWithLocationImpl::setComment( const OUString& Comment )
+{
+ StringResourcePersistenceImpl::setComment( Comment );
+}
+void StringResourceWithLocationImpl::storeToStorage( const Reference< XStorage >& Storage,
+ const OUString& NameBase, const OUString& Comment )
+{
+ StringResourcePersistenceImpl::storeToStorage( Storage, NameBase, Comment );
+}
+void StringResourceWithLocationImpl::storeToURL( const OUString& URL,
+ const OUString& NameBase, const OUString& Comment,
+ const Reference< css::task::XInteractionHandler >& Handler )
+{
+ StringResourcePersistenceImpl::storeToURL( URL, NameBase, Comment, Handler );
+}
+Sequence< ::sal_Int8 > StringResourceWithLocationImpl::exportBinary( )
+{
+ return StringResourcePersistenceImpl::exportBinary();
+}
+void StringResourceWithLocationImpl::importBinary( const Sequence< ::sal_Int8 >& Data )
+{
+ StringResourcePersistenceImpl::importBinary( Data );
+}
+
+
+// XStringResourceWithLocation
+
+// XStringResourceWithLocation
+void StringResourceWithLocationImpl::storeAsURL( const OUString& URL )
+{
+ setURL( URL );
+ store();
+}
+
+void StringResourceWithLocationImpl::setURL( const OUString& URL )
+{
+ std::unique_lock aGuard( m_aMutex );
+ implCheckReadOnly( "StringResourceWithLocationImpl::setURL(): Read only" );
+
+ sal_Int32 nLen = URL.getLength();
+ if( nLen == 0 )
+ {
+ throw IllegalArgumentException( "StringResourceWithLocationImpl::setURL: invalid URL", Reference< XInterface >(), 0 );
+ }
+
+ implLoadAllLocales();
+
+ // Delete files at old location
+ implStoreAtLocation( m_aLocation, m_aNameBase, m_aComment,
+ getFileAccessImpl(), false/*bUsedForStore*/, false/*bStoreAll*/, true/*bKillAll*/ );
+
+ m_aLocation = URL;
+ m_bLocationChanged = true;
+}
+
+
+// Private helper methods
+
+
+// Scan locale properties files
+void StringResourceWithLocationImpl::implScanLocales()
+{
+ const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
+ if( xFileAccess.is() && xFileAccess->isFolder( m_aLocation ) )
+ {
+ Sequence< OUString > aContentSeq = xFileAccess->getFolderContents( m_aLocation, false );
+ implScanLocaleNames( aContentSeq );
+ }
+}
+
+// Loading
+bool StringResourceWithLocationImpl::implLoadLocale( LocaleItem* pLocaleItem )
+{
+ bool bSuccess = false;
+
+ const Reference< ucb::XSimpleFileAccess3 > xFileAccess = getFileAccessImpl();
+ if( xFileAccess.is() )
+ {
+ OUString aCompleteFileName =
+ implGetPathForLocaleItem( pLocaleItem, m_aNameBase, m_aLocation );
+
+ Reference< io::XInputStream > xInputStream;
+ try
+ {
+ xInputStream = xFileAccess->openFileRead( aCompleteFileName );
+ }
+ catch( Exception& )
+ {}
+ if( xInputStream.is() )
+ {
+ bSuccess = StringResourcePersistenceImpl::implReadPropertiesFile( pLocaleItem, xInputStream );
+ xInputStream->closeInput();
+ }
+ }
+
+ return bSuccess;
+}
+
+const Reference< ucb::XSimpleFileAccess3 > & StringResourceWithLocationImpl::getFileAccessImpl()
+{
+ if( !m_xSFI.is() )
+ {
+ m_xSFI = ucb::SimpleFileAccess::create(m_xContext);
+
+ if( m_xSFI.is() && m_xInteractionHandler.is() )
+ m_xSFI->setInteractionHandler( m_xInteractionHandler );
+ }
+ return m_xSFI;
+}
+
+} // namespace stringresource
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/stringresource/stringresource.hxx b/scripting/source/stringresource/stringresource.hxx
new file mode 100644
index 0000000000..50fd8567e0
--- /dev/null
+++ b/scripting/source/stringresource/stringresource.hxx
@@ -0,0 +1,486 @@
+/* -*- 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/resource/XStringResourceWithStorage.hpp>
+#include <com/sun/star/resource/XStringResourceWithLocation.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <mutex>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+
+namespace stringresource
+{
+
+
+// class stringresourceImpl
+
+
+// Hashtable to map string ids to string
+typedef std::unordered_map
+<
+ OUString,
+ OUString
+>
+IdToStringMap;
+
+typedef std::unordered_map
+<
+ OUString,
+ sal_Int32
+>
+IdToIndexMap;
+
+
+struct LocaleItem
+{
+ css::lang::Locale m_locale;
+ IdToStringMap m_aIdToStringMap;
+ IdToIndexMap m_aIdToIndexMap;
+ sal_Int32 m_nNextIndex;
+ bool m_bLoaded;
+ bool m_bModified;
+
+ LocaleItem( css::lang::Locale locale, bool bLoaded=true )
+ : m_locale(std::move( locale ))
+ , m_nNextIndex( 0 )
+ , m_bLoaded( bLoaded )
+ , m_bModified( false )
+ {}
+};
+
+typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::resource::XStringResourceManager > StringResourceImpl_BASE;
+
+class StringResourceImpl : public StringResourceImpl_BASE
+{
+protected:
+ std::mutex m_aMutex;
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ LocaleItem* m_pCurrentLocaleItem;
+ LocaleItem* m_pDefaultLocaleItem;
+ bool m_bDefaultModified;
+
+ ::comphelper::OInterfaceContainerHelper4<css::util::XModifyListener> m_aListenerContainer;
+
+ std::vector< std::unique_ptr<LocaleItem> > m_aLocaleItemVector;
+ std::vector< std::unique_ptr<LocaleItem> > m_aDeletedLocaleItemVector;
+ std::vector< std::unique_ptr<LocaleItem> > m_aChangedDefaultLocaleVector;
+
+ bool m_bModified;
+ bool m_bReadOnly;
+
+ sal_Int32 m_nNextUniqueNumericId;
+
+ // Scans ResourceID to start with number and adapt m_nNextUniqueNumericId
+ void implScanIdForNumber( const OUString& ResourceID );
+ const static sal_Int32 UNIQUE_NUMBER_NEEDS_INITIALISATION = -1;
+
+ // Checks read only status and throws exception if it's true
+ /// @throws css::lang::NoSupportException
+ void implCheckReadOnly( const char* pExceptionMsg );
+
+ // Returns the LocalItem for a given locale, if it exists, otherwise NULL
+ // This method compares the locales exactly, no closest match search is performed
+ /// @throws css::lang::IllegalArgumentException
+ LocaleItem* getItemForLocale( const css::lang::Locale& locale, bool bException );
+
+ // Returns the LocalItem for a given locale, if it exists, otherwise NULL
+ // This method performs a closest match search, at least the language must match
+ LocaleItem* getClosestMatchItemForLocale( const css::lang::Locale& locale );
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void implSetCurrentLocale( std::unique_lock<std::mutex>& rGuard, const css::lang::Locale& locale,
+ bool FindClosestMatch, bool bUseDefaultIfNoMatch );
+
+ void implModified(std::unique_lock<std::mutex>&);
+ void implNotifyListeners(std::unique_lock<std::mutex>&);
+
+ //=== Impl methods for ...ForLocale methods ===
+ /// @throws css::resource::MissingResourceException
+ OUString implResolveString( const OUString& ResourceID, LocaleItem* pLocaleItem );
+ bool implHasEntryForId( const OUString& ResourceID, LocaleItem* pLocaleItem );
+ css::uno::Sequence< OUString > implGetResourceIDs( LocaleItem* pLocaleItem );
+ void implSetString( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID,
+ const OUString& Str, LocaleItem* pLocaleItem );
+ /// @throws css::resource::MissingResourceException
+ void implRemoveId( std::unique_lock<std::mutex>& rGuard, const OUString& ResourceID, LocaleItem* pLocaleItem );
+
+ // Method to load a locale if necessary, returns true if loading was
+ // successful. Default implementation in base class just returns true.
+ virtual bool loadLocale( LocaleItem* pLocaleItem );
+
+ virtual void implLoadAllLocales();
+
+public:
+ explicit StringResourceImpl(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~StringResourceImpl() 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;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XStringResourceResolver
+ virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override;
+ virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override;
+ virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale
+ ( const css::lang::Locale& locale ) override;
+ virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override;
+ virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override;
+ virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override;
+
+ // XStringResourceManager
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+ virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override;
+ virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override;
+ virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeId( const OUString& ResourceID ) override;
+ virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override;
+ virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override;
+ };
+
+typedef ::cppu::ImplInheritanceHelper<
+ StringResourceImpl,
+ css::resource::XStringResourcePersistence > StringResourcePersistenceImpl_BASE;
+
+class BinaryOutput;
+
+class StringResourcePersistenceImpl : public StringResourcePersistenceImpl_BASE
+{
+protected:
+ OUString m_aNameBase;
+ OUString m_aComment;
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void implInitializeCommonParameters( std::unique_lock<std::mutex>& rGuard, const css::uno::Sequence< css::uno::Any >& aArguments );
+
+ // Scan locale properties files
+ virtual void implScanLocales();
+
+ // Method to load a locale if necessary, returns true if loading was successful
+ virtual bool loadLocale( LocaleItem* pLocaleItem ) override;
+
+ // does the actual loading
+ virtual bool implLoadLocale( LocaleItem* pLocaleItem );
+
+ virtual void implLoadAllLocales() override;
+
+ void implScanLocaleNames( const css::uno::Sequence< OUString >& aContentSeq );
+ static OUString implGetFileNameForLocaleItem( LocaleItem const * pLocaleItem, const OUString& aNameBase );
+ static OUString implGetPathForLocaleItem( LocaleItem const * pLocaleItem, const OUString& aNameBase,
+ std::u16string_view aLocation, bool bDefaultFile=false );
+
+ bool implReadPropertiesFile( LocaleItem* pLocaleItem,
+ const css::uno::Reference< css::io::XInputStream >& xInput );
+
+ bool implWritePropertiesFile( LocaleItem const * pLocaleItem,
+ const css::uno::Reference< css::io::XOutputStream >& xOutputStream,
+ const OUString& aComment );
+
+ void implWriteLocaleBinary( LocaleItem* pLocaleItem, BinaryOutput& rOut );
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void implStoreAtStorage
+ (
+ const OUString& aNameBase,
+ const OUString& aComment,
+ const css::uno::Reference< css::embed::XStorage >& Storage,
+ bool bUsedForStore,
+ bool bStoreAll
+ );
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void implKillRemovedLocaleFiles
+ (
+ std::u16string_view Location,
+ const OUString& aNameBase,
+ const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
+ );
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void implKillChangedDefaultFiles
+ (
+ std::u16string_view Location,
+ const OUString& aNameBase,
+ const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess
+ );
+
+ /// @throws css::uno::Exception
+ /// @throws css::uno::RuntimeException
+ void implStoreAtLocation
+ (
+ std::u16string_view Location,
+ const OUString& aNameBase,
+ const OUString& aComment,
+ const css::uno::Reference< css::ucb::XSimpleFileAccess3 >& xFileAccess,
+ bool bUsedForStore,
+ bool bStoreAll,
+ bool bKillAll = false
+ );
+
+public:
+ explicit StringResourcePersistenceImpl(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~StringResourcePersistenceImpl() 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;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XStringResourceResolver
+ virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override;
+ virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override;
+ virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale
+ ( const css::lang::Locale& locale ) override;
+ virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override;
+ virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override;
+ virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override;
+
+ // XStringResourceManager
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+ virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override;
+ virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override;
+ virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeId( const OUString& ResourceID ) override;
+ virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override;
+ virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override;
+
+ // XStringResourcePersistence
+ virtual void SAL_CALL store( ) override;
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setComment( const OUString& Comment ) override;
+ virtual void SAL_CALL storeToStorage
+ ( const css::uno::Reference< css::embed::XStorage >& Storage,
+ const OUString& NameBase, const OUString& Comment ) override;
+ virtual void SAL_CALL storeToURL( const OUString& URL, const OUString& NameBase,
+ const OUString& Comment, const css::uno::Reference
+ < css::task::XInteractionHandler >& Handler ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL exportBinary( ) override;
+ virtual void SAL_CALL importBinary( const css::uno::Sequence< ::sal_Int8 >& Data ) override;
+};
+
+
+typedef ::cppu::ImplInheritanceHelper<
+ StringResourcePersistenceImpl,
+ css::lang::XInitialization,
+ css::resource::XStringResourceWithStorage > StringResourceWithStorageImpl_BASE;
+
+class StringResourceWithStorageImpl : public StringResourceWithStorageImpl_BASE
+{
+ css::uno::Reference< css::embed::XStorage > m_xStorage;
+ bool m_bStorageChanged;
+
+ virtual void implScanLocales() override;
+ virtual bool implLoadLocale( LocaleItem* pLocaleItem ) override;
+
+public:
+ explicit StringResourceWithStorageImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~StringResourceWithStorageImpl() 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XStringResourceResolver
+ virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override;
+ virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override;
+ virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale
+ ( const css::lang::Locale& locale ) override;
+ virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override;
+ virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override;
+ virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override;
+
+ // XStringResourceManager
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+ virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override;
+ virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override;
+ virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeId( const OUString& ResourceID ) override;
+ virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override;
+ virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override;
+
+ // XStringResourcePersistence
+ virtual void SAL_CALL store( ) override;
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setComment( const OUString& Comment ) override;
+ virtual void SAL_CALL storeToStorage
+ ( const css::uno::Reference< css::embed::XStorage >& Storage,
+ const OUString& NameBase, const OUString& Comment ) override;
+ virtual void SAL_CALL storeToURL( const OUString& URL, const OUString& NameBase,
+ const OUString& Comment, const css::uno::Reference
+ < css::task::XInteractionHandler >& Handler ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL exportBinary( ) override;
+ virtual void SAL_CALL importBinary( const css::uno::Sequence< ::sal_Int8 >& Data ) override;
+
+ // XStringResourceWithStorage
+ virtual void SAL_CALL storeAsStorage
+ ( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
+ virtual void SAL_CALL setStorage
+ ( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
+};
+
+
+typedef ::cppu::ImplInheritanceHelper<
+ StringResourcePersistenceImpl,
+ css::lang::XInitialization,
+ css::resource::XStringResourceWithLocation > StringResourceWithLocationImpl_BASE;
+
+class StringResourceWithLocationImpl : public StringResourceWithLocationImpl_BASE
+{
+ OUString m_aLocation;
+ bool m_bLocationChanged;
+ css::uno::Reference< css::ucb::XSimpleFileAccess3 > m_xSFI;
+ css::uno::Reference< css::task::XInteractionHandler > m_xInteractionHandler;
+
+ const css::uno::Reference< css::ucb::XSimpleFileAccess3 > & getFileAccessImpl();
+
+ virtual void implScanLocales() override;
+ virtual bool implLoadLocale( LocaleItem* pLocaleItem ) override;
+
+public:
+ explicit StringResourceWithLocationImpl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~StringResourceWithLocationImpl() 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XStringResourceResolver
+ virtual OUString SAL_CALL resolveString( const OUString& ResourceID ) override;
+ virtual OUString SAL_CALL resolveStringForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual sal_Bool SAL_CALL hasEntryForId( const OUString& ResourceID ) override;
+ virtual sal_Bool SAL_CALL hasEntryForIdAndLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDs( ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getResourceIDsForLocale
+ ( const css::lang::Locale& locale ) override;
+ virtual css::lang::Locale SAL_CALL getCurrentLocale( ) override;
+ virtual css::lang::Locale SAL_CALL getDefaultLocale( ) override;
+ virtual css::uno::Sequence< css::lang::Locale > SAL_CALL getLocales( ) override;
+
+ // XStringResourceManager
+ virtual sal_Bool SAL_CALL isReadOnly() override;
+ virtual void SAL_CALL setCurrentLocale( const css::lang::Locale& locale, sal_Bool FindClosestMatch ) override;
+ virtual void SAL_CALL setDefaultLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL setString( const OUString& ResourceID, const OUString& Str ) override;
+ virtual void SAL_CALL setStringForLocale( const OUString& ResourceID, const OUString& Str,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeId( const OUString& ResourceID ) override;
+ virtual void SAL_CALL removeIdForLocale( const OUString& ResourceID,
+ const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL newLocale( const css::lang::Locale& locale ) override;
+ virtual void SAL_CALL removeLocale( const css::lang::Locale& locale ) override;
+ virtual ::sal_Int32 SAL_CALL getUniqueNumericId( ) override;
+
+ // XStringResourcePersistence
+ virtual void SAL_CALL store( ) override;
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setComment( const OUString& Comment ) override;
+ virtual void SAL_CALL storeToStorage
+ ( const css::uno::Reference< css::embed::XStorage >& Storage,
+ const OUString& NameBase, const OUString& Comment ) override;
+ virtual void SAL_CALL storeToURL( const OUString& URL, const OUString& NameBase,
+ const OUString& Comment, const css::uno::Reference
+ < css::task::XInteractionHandler >& Handler ) override;
+ virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL exportBinary( ) override;
+ virtual void SAL_CALL importBinary( const css::uno::Sequence< ::sal_Int8 >& Data ) override;
+
+ // XStringResourceWithLocation
+ virtual void SAL_CALL storeAsURL( const OUString& URL ) override;
+ virtual void SAL_CALL setURL( const OUString& URL ) override;
+};
+
+
+} // namespace stringtable
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/vbaevents/eventhelper.cxx b/scripting/source/vbaevents/eventhelper.cxx
new file mode 100644
index 0000000000..9f4bdc1f72
--- /dev/null
+++ b/scripting/source/vbaevents/eventhelper.cxx
@@ -0,0 +1,983 @@
+/* -*- 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/macros.h>
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/uno3.hxx>
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/propertycontainer.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <ooo/vba/XVBAToOOEventDescGen.hpp>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+#include <com/sun/star/util/XCloseListener.hpp>
+#include <com/sun/star/util/XCloseBroadcaster.hpp>
+
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp>
+
+#include <com/sun/star/container/XNamed.hpp>
+
+#include <com/sun/star/drawing/XControlShape.hpp>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XDialog.hpp>
+#include <com/sun/star/awt/KeyEvent.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <com/sun/star/awt/XFixedText.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XComboBox.hpp>
+#include <com/sun/star/awt/XRadioButton.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <basic/basmgr.hxx>
+#include <filter/msfilter/msvbahelper.hxx>
+#include <vbahelper/vbareturntypes.hxx>
+
+#include <com/sun/star/script/XScriptListener.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/evtmethodhelper.hxx>
+
+#include <vector>
+#include <unordered_map>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::uno;
+using namespace ::ooo::vba;
+
+// Some constants
+constexpr std::u16string_view DELIM = u"::";
+constexpr sal_Int32 DELIMLEN = DELIM.size();
+
+static bool isKeyEventOk( awt::KeyEvent& evt, const Sequence< Any >& params )
+{
+ return params.hasElements() && ( params[ 0 ] >>= evt );
+}
+
+static bool isMouseEventOk( awt::MouseEvent& evt, const Sequence< Any >& params )
+{
+ return params.hasElements() && ( params[ 0 ] >>= evt );
+}
+
+static Sequence< Any > ooMouseEvtToVBADblClick( const Sequence< Any >& params )
+{
+ awt::MouseEvent evt;
+
+ if ( !( isMouseEventOk(evt, params)) ||
+ (evt.ClickCount != 2) )
+ return Sequence< Any >();
+ // give back orig params, this will signal that the event is good
+ return params;
+}
+
+static Sequence< Any > ooMouseEvtToVBAMouseEvt( const Sequence< Any >& params )
+{
+ awt::MouseEvent evt;
+
+ if ( !isMouseEventOk(evt, params) )
+ return Sequence< Any >();
+
+ Sequence< Any > translatedParams{ Any(evt.Buttons), // Buttons
+ Any(evt.Modifiers), // Shift
+ Any(evt.X), // X
+ Any(evt.Y) }; // Y
+ return translatedParams;
+}
+
+static Sequence< Any > ooKeyPressedToVBAKeyPressed( const Sequence< Any >& params )
+{
+ awt::KeyEvent evt;
+
+ if ( !isKeyEventOk( evt, params ) )
+ return Sequence< Any >();
+
+ Reference< msforms::XReturnInteger> xKeyCode = new ReturnInteger( sal_Int32( evt.KeyCode ) );
+ Sequence< Any > translatedParams{ Any(xKeyCode) };
+ return translatedParams;
+}
+
+static Sequence< Any > ooKeyPressedToVBAKeyUpDown( const Sequence< Any >& params )
+{
+ awt::KeyEvent evt;
+
+ if ( !isKeyEventOk( evt, params ) )
+ return Sequence< Any >();
+
+ Reference< msforms::XReturnInteger> xKeyCode = new ReturnInteger( evt.KeyCode );
+ sal_Int8 shift = sal::static_int_cast<sal_Int8>( evt.Modifiers );
+
+ // #TODO check whether values from OOO conform to values generated from vba
+ Sequence< Any > translatedParams{ Any(xKeyCode), Any(shift) };
+ return translatedParams;
+}
+
+typedef Sequence< Any > (*Translator)(const Sequence< Any >&);
+
+namespace {
+
+//expand the "TranslateInfo" struct to support more kinds of events
+struct TranslateInfo
+{
+ OUString sVBAName; //vba event name
+ Translator toVBA; //the method to convert OO event parameters to VBA event parameters
+ bool (*ApproveRule)(const ScriptEvent& evt, void const * pPara); //this method is used to determine which types of controls should execute the event
+ void const *pPara; //Parameters for the above approve method
+};
+
+}
+
+typedef std::unordered_map<
+ OUString,
+ std::vector< TranslateInfo > > EventInfoHash;
+
+namespace {
+
+struct TranslatePropMap
+{
+ OUString sEventInfo; //OO event name
+ TranslateInfo aTransInfo;
+};
+
+}
+
+static bool ApproveAll(const ScriptEvent& evt, void const * pPara); //allow all types of controls to execute the event
+static bool ApproveType(const ScriptEvent& evt, void const * pPara); //certain types of controls should execute the event, those types are given by pPara
+static bool DenyType(const ScriptEvent& evt, void const * pPara); //certain types of controls should not execute the event, those types are given by pPara
+static bool DenyMouseDrag(const ScriptEvent& evt, void const * pPara); //used for VBA MouseMove event when "Shift" key is pressed
+
+namespace {
+
+struct TypeList
+{
+ uno::Type const * pTypeList;
+ int nListLength;
+};
+
+}
+
+Type const typeXFixedText = cppu::UnoType<awt::XFixedText>::get();
+Type const typeXTextComponent = cppu::UnoType<awt::XTextComponent>::get();
+Type const typeXComboBox = cppu::UnoType<awt::XComboBox>::get();
+Type const typeXRadioButton = cppu::UnoType<awt::XRadioButton>::get();
+Type const typeXListBox = cppu::UnoType<awt::XListBox>::get();
+
+
+TypeList const fixedTextList = {&typeXFixedText, 1};
+TypeList const textCompList = {&typeXTextComponent, 1};
+TypeList const radioButtonList = {&typeXRadioButton, 1};
+TypeList const comboBoxList = {&typeXComboBox, 1};
+TypeList const listBoxList = {&typeXListBox, 1};
+
+//this array stores the OO event to VBA event translation info
+static TranslatePropMap aTranslatePropMap_Impl[] =
+{
+ { OUString("actionPerformed"), { OUString("_Change"), nullptr, DenyType, static_cast<void const *>(&radioButtonList) } },
+ // actionPerformed ooo event
+ { OUString("actionPerformed"), { OUString("_Click"), nullptr, ApproveAll, nullptr } },
+ { OUString("itemStateChanged"), { OUString("_Change"), nullptr, ApproveType, static_cast<void const *>(&radioButtonList) } },
+ // itemStateChanged ooo event
+ { OUString("itemStateChanged"), { OUString("_Click"), nullptr, ApproveType, static_cast<void const *>(&comboBoxList) } },
+
+ { OUString("itemStateChanged"), { OUString("_Click"), nullptr, ApproveType, static_cast<void const *>(&listBoxList) } },
+ // changed ooo event
+ { OUString("changed"), { OUString("_Change"), nullptr, ApproveAll, nullptr } },
+
+ // focusGained ooo event
+ { OUString("focusGained"), { OUString("_GotFocus"), nullptr, ApproveAll, nullptr } },
+
+ // focusLost ooo event
+ { OUString("focusLost"), { OUString("_LostFocus"), nullptr, ApproveAll, nullptr } },
+ { OUString("focusLost"), { OUString("_Exit"), nullptr, ApproveType, static_cast<void const *>(&textCompList) } }, // support VBA TextBox_Exit event
+
+ // adjustmentValueChanged ooo event
+ { OUString("adjustmentValueChanged"), { OUString("_Scroll"), nullptr, ApproveAll, nullptr } },
+ { OUString("adjustmentValueChanged"), { OUString("_Change"), nullptr, ApproveAll, nullptr } },
+
+ // textChanged ooo event
+ { OUString("textChanged"), { OUString("_Change"), nullptr, ApproveAll, nullptr } },
+
+ // keyReleased ooo event
+ { OUString("keyReleased"), { OUString("_KeyUp"), ooKeyPressedToVBAKeyUpDown, ApproveAll, nullptr } },
+
+ // mouseReleased ooo event
+ { OUString("mouseReleased"), { OUString("_Click"), ooMouseEvtToVBAMouseEvt, ApproveType, static_cast<void const *>(&fixedTextList) } },
+ { OUString("mouseReleased"), { OUString("_MouseUp"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } },
+
+ // mousePressed ooo event
+ { OUString("mousePressed"), { OUString("_MouseDown"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } },
+ { OUString("mousePressed"), { OUString("_DblClick"), ooMouseEvtToVBADblClick, ApproveAll, nullptr } },
+
+ // mouseMoved ooo event
+ { OUString("mouseMoved"), { OUString("_MouseMove"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } },
+ { OUString("mouseDragged"), { OUString("_MouseMove"), ooMouseEvtToVBAMouseEvt, DenyMouseDrag, nullptr } },
+
+ // keyPressed ooo event
+ { OUString("keyPressed"), { OUString("_KeyDown"), ooKeyPressedToVBAKeyUpDown, ApproveAll, nullptr } },
+ { OUString("keyPressed"), { OUString("_KeyPress"), ooKeyPressedToVBAKeyPressed, ApproveAll, nullptr } }
+};
+
+static EventInfoHash& getEventTransInfo()
+{
+ static EventInfoHash eventTransInfo = []()
+ {
+ EventInfoHash tmp;
+ OUString sEventInfo;
+ TranslatePropMap* pTransProp = aTranslatePropMap_Impl;
+ int nCount = SAL_N_ELEMENTS(aTranslatePropMap_Impl);
+
+ int i = 0;
+ while (i < nCount)
+ {
+ sEventInfo = pTransProp->sEventInfo;
+ std::vector< TranslateInfo > infoList;
+ do
+ {
+ infoList.push_back( pTransProp->aTransInfo );
+ pTransProp++;
+ i++;
+ }while(i < nCount && sEventInfo == pTransProp->sEventInfo);
+ tmp[sEventInfo] = std::move(infoList);
+ }
+ return tmp;
+ }();
+ return eventTransInfo;
+}
+
+
+// Helper class
+
+namespace {
+
+class ScriptEventHelper
+{
+public:
+ explicit ScriptEventHelper( const Reference< XInterface >& xControl );
+ explicit ScriptEventHelper( const OUString& sCntrlServiceName );
+ ~ScriptEventHelper();
+ Sequence< ScriptEventDescriptor > createEvents( const OUString& sCodeName );
+ Sequence< OUString > getEventListeners() const;
+private:
+ Reference< XComponentContext > m_xCtx;
+ Reference< XInterface > m_xControl;
+ bool m_bDispose;
+};
+
+}
+
+static bool
+eventMethodToDescriptor( std::u16string_view rEventMethod, ScriptEventDescriptor& evtDesc, const OUString& sCodeName )
+{
+ // format of ControlListener is TypeName::methodname e.g.
+ // "com.sun.star.awt.XActionListener::actionPerformed" or
+ // "XActionListener::actionPerformed
+
+ OUString sMethodName;
+ OUString sTypeName;
+ size_t nDelimPos = rEventMethod.find( DELIM );
+ if ( nDelimPos == std::u16string_view::npos )
+ {
+ return false;
+ }
+ sMethodName = rEventMethod.substr( nDelimPos + DELIMLEN );
+ sTypeName = rEventMethod.substr( 0, nDelimPos );
+
+ EventInfoHash& infos = getEventTransInfo();
+
+ // Only create an ScriptEventDescriptor for an event we can translate
+ // or emulate
+ if ( !sMethodName.isEmpty()
+ && !sTypeName.isEmpty()
+ && ( infos.find( sMethodName ) != infos.end() ) )
+ {
+ // just fill in CodeName, when the event fires the other
+ // info is gathered from the event source to determine what
+ // event handler we try to call
+ evtDesc.ScriptCode = sCodeName;
+ evtDesc.ListenerType = sTypeName;
+ evtDesc.EventMethod = sMethodName;
+
+ // set this it VBAInterop, ensures that it doesn't
+ // get persisted or shown in property editors
+ evtDesc.ScriptType = "VBAInterop";
+ return true;
+ }
+ return false;
+
+}
+
+ScriptEventHelper::ScriptEventHelper( const Reference< XInterface >& xControl ) :
+ m_xCtx( comphelper::getProcessComponentContext() ),
+ m_xControl( xControl ),
+ m_bDispose( false )
+{}
+
+ScriptEventHelper::ScriptEventHelper( const OUString& sCntrlServiceName ) :
+ m_xCtx( comphelper::getProcessComponentContext() ),
+ m_bDispose( true )
+{
+ m_xControl.set( m_xCtx->getServiceManager()->createInstanceWithContext( sCntrlServiceName, m_xCtx ), uno::UNO_QUERY );
+}
+
+ScriptEventHelper::~ScriptEventHelper()
+{
+ // dispose control ( and remove any associated event registrations )
+ if ( m_bDispose )
+ {
+ try
+ {
+ uno::Reference< lang::XComponent > xComp( m_xControl, uno::UNO_QUERY );
+ if (xComp)
+ xComp->dispose();
+ }
+ // destructor can't throw
+ catch( uno::Exception& )
+ {
+ }
+ }
+}
+
+Sequence< OUString >
+ScriptEventHelper::getEventListeners() const
+{
+ std::vector< OUString > eventMethods;
+
+ Reference< beans::XIntrospection > xIntrospection = beans::theIntrospection::get( m_xCtx );
+
+ Reference< beans::XIntrospectionAccess > xIntrospectionAccess =
+ xIntrospection->inspect( Any( m_xControl ) );
+ const Sequence< Type > aControlListeners =
+ xIntrospectionAccess->getSupportedListeners();
+ for ( const Type& listType : aControlListeners )
+ {
+ OUString sFullTypeName = listType.getTypeName();
+ const Sequence< OUString > sMeths =
+ comphelper::getEventMethodsForType( listType );
+ std::transform(sMeths.begin(), sMeths.end(), std::back_inserter(eventMethods),
+ [&sFullTypeName](const OUString& rMeth) -> OUString { return sFullTypeName + DELIM + rMeth; });
+ }
+
+ return comphelper::containerToSequence(eventMethods);
+}
+
+Sequence< ScriptEventDescriptor >
+ScriptEventHelper::createEvents( const OUString& sCodeName )
+{
+ const Sequence< OUString > aControlListeners = getEventListeners();
+ sal_Int32 nLength = aControlListeners.getLength();
+
+ Sequence< ScriptEventDescriptor > aDest( nLength );
+ sal_Int32 nEvts = 0;
+ for ( OUString const & i : aControlListeners)
+ {
+ // from getListeners eventName is of form
+ // "com.sun.star.awt.XActionListener::actionPerformed"
+ // we need to strip "com.sun.star.awt." from that for form
+ // controls
+ ScriptEventDescriptor evtDesc;
+ if ( eventMethodToDescriptor( i, evtDesc, sCodeName ) )
+ {
+ sal_Int32 dIndex = nEvts;
+ ++nEvts;
+ if ( nEvts > aDest.getLength() )
+ aDest.realloc( nEvts );// should never happen
+ aDest.getArray()[ dIndex ] = evtDesc;
+ }
+ }
+ aDest.realloc( nEvts );
+
+ return aDest;
+}
+
+
+typedef ::cppu::WeakImplHelper< container::XNameContainer > NameContainer_BASE;
+
+namespace {
+
+class ReadOnlyEventsNameContainer : public NameContainer_BASE
+{
+public:
+ ReadOnlyEventsNameContainer( const Sequence< OUString >& eventMethods, const OUString& sCodeName );
+ // XNameContainer
+
+ virtual void SAL_CALL insertByName( const OUString&, const Any& ) override
+ {
+ throw RuntimeException("ReadOnly container" );
+
+ }
+ virtual void SAL_CALL removeByName( const OUString& ) override
+ {
+ throw RuntimeException("ReadOnly container" );
+ }
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString&, const Any& ) override
+ {
+ throw RuntimeException("ReadOnly container" );
+
+ }
+
+ // XNameAccess
+ virtual Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual Type SAL_CALL getElementType( ) override
+ { return cppu::UnoType<OUString>::get(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ { return !m_hEvents.empty(); }
+private:
+
+typedef std::unordered_map< OUString, Any > EventSupplierHash;
+
+ EventSupplierHash m_hEvents;
+};
+
+}
+
+ReadOnlyEventsNameContainer::ReadOnlyEventsNameContainer( const Sequence< OUString >& eventMethods, const OUString& sCodeName )
+{
+ for ( const OUString& rSrc : eventMethods )
+ {
+ Any aDesc;
+ ScriptEventDescriptor evtDesc;
+ if ( eventMethodToDescriptor( rSrc, evtDesc, sCodeName ) )
+ {
+ aDesc <<= evtDesc;
+ m_hEvents[ rSrc ] = aDesc;
+ }
+ }
+}
+
+Any SAL_CALL
+ReadOnlyEventsNameContainer::getByName( const OUString& aName ){
+ EventSupplierHash::const_iterator it = m_hEvents.find( aName );
+ if ( it == m_hEvents.end() )
+ throw container::NoSuchElementException();
+ return it->second;
+}
+
+Sequence< OUString > SAL_CALL
+ReadOnlyEventsNameContainer::getElementNames( )
+{
+ return comphelper::mapKeysToSequence(m_hEvents);
+}
+
+sal_Bool SAL_CALL
+ReadOnlyEventsNameContainer::hasByName( const OUString& aName )
+{
+ EventSupplierHash::const_iterator it = m_hEvents.find( aName );
+ if ( it == m_hEvents.end() )
+ return false;
+ return true;
+}
+
+namespace {
+
+class ReadOnlyEventsSupplier : public ::cppu::WeakImplHelper< XScriptEventsSupplier >
+{
+public:
+ ReadOnlyEventsSupplier( const Sequence< OUString >& eventMethods, const OUString& sCodeName )
+ { m_xNameContainer = new ReadOnlyEventsNameContainer( eventMethods, sCodeName ); }
+
+ // XScriptEventSupplier
+ virtual Reference< container::XNameContainer > SAL_CALL getEvents( ) override { return m_xNameContainer; }
+private:
+ Reference< container::XNameContainer > m_xNameContainer;
+};
+
+}
+
+typedef ::cppu::WeakImplHelper< XScriptListener, util::XCloseListener, lang::XInitialization, css::lang::XServiceInfo > EventListener_BASE;
+
+#define EVENTLSTNR_PROPERTY_ID_MODEL 1
+constexpr OUStringLiteral EVENTLSTNR_PROPERTY_MODEL = u"Model";
+
+namespace {
+
+class EventListener : public EventListener_BASE
+ ,public ::comphelper::OMutexAndBroadcastHelper
+ ,public ::comphelper::OPropertyContainer
+ ,public ::comphelper::OPropertyArrayUsageHelper< EventListener >
+{
+
+public:
+ EventListener();
+ // XEventListener
+ virtual void SAL_CALL disposing(const lang::EventObject& Source) override;
+ using cppu::OPropertySetHelper::disposing;
+
+ // XScriptListener
+ virtual void SAL_CALL firing(const ScriptEvent& evt) override;
+ virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override;
+ // XCloseListener
+ virtual void SAL_CALL queryClosing( const lang::EventObject& Source, sal_Bool GetsOwnership ) override;
+ virtual void SAL_CALL notifyClosing( const lang::EventObject& Source ) override;
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+ // XInterface
+ DECLARE_XINTERFACE()
+
+ // XTypeProvider
+ DECLARE_XTYPEPROVIDER()
+ virtual void SAL_CALL setFastPropertyValue( sal_Int32 nHandle, const css::uno::Any& rValue ) override
+ {
+ if ( nHandle == EVENTLSTNR_PROPERTY_ID_MODEL )
+ {
+ uno::Reference< frame::XModel > xModel( rValue, uno::UNO_QUERY );
+ if( xModel != m_xModel)
+ {
+ // Remove the listener from the old XCloseBroadcaster.
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xModel, uno::UNO_QUERY );
+ if (xCloseBroadcaster.is())
+ {
+ xCloseBroadcaster->removeCloseListener( this );
+ }
+ // Add the listener into the new XCloseBroadcaster.
+ xCloseBroadcaster.set( xModel, uno::UNO_QUERY );
+ if (xCloseBroadcaster.is())
+ {
+ xCloseBroadcaster->addCloseListener( this );
+ }
+ }
+ }
+ OPropertyContainer::setFastPropertyValue( nHandle, rValue );
+ if ( nHandle == EVENTLSTNR_PROPERTY_ID_MODEL )
+ setShellFromModel();
+ }
+
+ OUString SAL_CALL getImplementationName() override
+ {
+ return "ooo.vba.EventListener";
+ }
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return { getImplementationName() };
+ }
+
+protected:
+ // OPropertySetHelper
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+private:
+ void setShellFromModel();
+ /// @throws RuntimeException
+ void firing_Impl( const ScriptEvent& evt, Any *pSyncRet );
+
+ Reference< frame::XModel > m_xModel;
+ bool m_bDocClosed;
+ SfxObjectShell* mpShell;
+};
+
+}
+
+EventListener::EventListener() :
+OPropertyContainer(GetBroadcastHelper()), m_bDocClosed(false), mpShell( nullptr )
+{
+ registerProperty( EVENTLSTNR_PROPERTY_MODEL, EVENTLSTNR_PROPERTY_ID_MODEL,
+ beans::PropertyAttribute::TRANSIENT, &m_xModel, cppu::UnoType<decltype(m_xModel)>::get() );
+}
+
+void
+EventListener::setShellFromModel()
+{
+ // reset mpShell
+ mpShell = nullptr;
+ SfxObjectShell* pShell = SfxObjectShell::GetFirst();
+ while ( m_xModel.is() && pShell )
+ {
+ if ( pShell->GetModel() == m_xModel )
+ {
+ mpShell = pShell;
+ break;
+ }
+ pShell = SfxObjectShell::GetNext( *pShell );
+ }
+}
+
+//XEventListener
+void
+EventListener::disposing(const lang::EventObject&)
+{
+}
+
+//XScriptListener
+
+void SAL_CALL
+EventListener::firing(const ScriptEvent& evt)
+{
+ firing_Impl( evt, nullptr );
+}
+
+Any SAL_CALL
+EventListener::approveFiring(const ScriptEvent& evt)
+{
+ Any ret;
+ firing_Impl( evt, &ret );
+ return ret;
+}
+
+// XCloseListener
+void SAL_CALL
+EventListener::queryClosing( const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ )
+{
+ //Nothing to do
+}
+
+void SAL_CALL
+EventListener::notifyClosing( const lang::EventObject& /*Source*/ )
+{
+ m_bDocClosed = true;
+ uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xModel, uno::UNO_QUERY );
+ if (xCloseBroadcaster.is())
+ {
+ xCloseBroadcaster->removeCloseListener( this );
+ }
+}
+
+// XInitialization
+void SAL_CALL
+EventListener::initialize( const Sequence< Any >& aArguments )
+{
+ if ( aArguments.getLength() == 1 )
+ aArguments[0] >>= m_xModel;
+ SAL_INFO(
+ "scripting",
+ "args " << aArguments.getLength() << " m_xModel " << m_xModel.is());
+}
+
+// XInterface
+
+IMPLEMENT_FORWARD_XINTERFACE2( EventListener, EventListener_BASE, OPropertyContainer )
+
+// XTypeProvider
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( EventListener, EventListener_BASE, OPropertyContainer )
+
+// OPropertySetHelper
+
+::cppu::IPropertyArrayHelper&
+EventListener::getInfoHelper( )
+{
+ return *getArrayHelper();
+}
+
+// OPropertyArrayUsageHelper
+
+::cppu::IPropertyArrayHelper*
+EventListener::createArrayHelper( ) const
+{
+ Sequence< beans::Property > aProps;
+ describeProperties( aProps );
+ return new ::cppu::OPropertyArrayHelper( aProps );
+}
+
+// XPropertySet
+Reference< beans::XPropertySetInfo >
+EventListener::getPropertySetInfo( )
+{
+ Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+
+
+//decide if the control should execute the event
+bool ApproveAll(SAL_UNUSED_PARAMETER const ScriptEvent&, SAL_UNUSED_PARAMETER void const * )
+{
+ return true;
+}
+
+//for the given control type in evt.Arguments[0], look for if it appears in the type list in pPara
+static bool FindControl(const ScriptEvent& evt, void const * pPara)
+{
+ lang::EventObject aEvent;
+ evt.Arguments[ 0 ] >>= aEvent;
+ uno::Reference< uno::XInterface > xInterface( aEvent.Source, uno::UNO_QUERY );
+
+ TypeList const * pTypeListInfo = static_cast<TypeList const *>(pPara);
+ Type const * pType = pTypeListInfo->pTypeList;
+ int nLen = pTypeListInfo->nListLength;
+
+ for (int i = 0; i < nLen; i++)
+ {
+ if ( xInterface->queryInterface( *pType ).hasValue() )
+ {
+ return true;
+ }
+ pType++;
+ }
+
+ return false;
+}
+
+//if the given control type in evt.Arguments[0] appears in the type list in pPara, then approve the execution
+bool ApproveType(const ScriptEvent& evt, void const * pPara)
+{
+ return FindControl(evt, pPara);
+}
+
+//if the given control type in evt.Arguments[0] appears in the type list in pPara, then deny the execution
+bool DenyType(const ScriptEvent& evt, void const * pPara)
+{
+ return !FindControl(evt, pPara);
+}
+
+//when mouse is moving, either the mouse button is pressed or some key is pressed can trigger the OO mouseDragged event,
+//the former should be denied, and the latter allowed, only by doing so can the VBA MouseMove event when the "Shift" key is
+//pressed can be correctly triggered
+bool DenyMouseDrag(const ScriptEvent& evt, SAL_UNUSED_PARAMETER void const * )
+{
+ awt::MouseEvent aEvent;
+ evt.Arguments[ 0 ] >>= aEvent;
+ return aEvent.Buttons == 0;
+}
+
+
+// EventListener
+
+void
+EventListener::firing_Impl(const ScriptEvent& evt, Any* pRet )
+{
+ // let default handlers deal with non vba stuff
+ if ( evt.ScriptType != "VBAInterop" )
+ return;
+ lang::EventObject aEvent;
+ evt.Arguments[ 0 ] >>= aEvent;
+ OUString sName = "UserForm";
+
+ uno::Reference< awt::XDialog > xDlg( aEvent.Source, uno::UNO_QUERY );
+ if ( !xDlg.is() )
+ {
+ // evt.Source is
+ // a) Dialog
+ // b) xShapeControl ( from api (sheet control) )
+ // c) eventmanager ( I guess )
+ // d) vba control ( from api also )
+ uno::Reference< drawing::XControlShape > xCntrlShape( evt.Source, uno::UNO_QUERY );
+ uno::Reference< awt::XControl > xControl( aEvent.Source, uno::UNO_QUERY );
+ if ( xCntrlShape.is() )
+ {
+ // for sheet controls ( that fire from the api ) we don't
+ // have the real control ( that's only available from the view )
+ // api code creates just a control instance that is transferred
+ // via aEvent.Arguments[ 0 ] that control though has no
+ // info like name etc.
+ uno::Reference< container::XNamed > xName( xCntrlShape->getControl(), uno::UNO_QUERY_THROW );
+ sName = xName->getName();
+ }
+ else
+ {
+ // Userform control ( fired from the api or from event manager )
+ uno::Reference< beans::XPropertySet > xProps;
+ xProps.set( xControl->getModel(), uno::UNO_QUERY_THROW );
+ xProps->getPropertyValue("Name") >>= sName;
+ }
+ }
+ //dumpEvent( evt );
+ EventInfoHash& infos = getEventTransInfo();
+ EventInfoHash::const_iterator eventInfo_it = infos.find( evt.MethodName );
+ EventInfoHash::const_iterator it_end = infos.end();
+ if ( eventInfo_it == it_end )
+ {
+ SAL_WARN("scripting", "Bogus event for " << evt.ScriptType );
+ return;
+ }
+
+ uno::Reference< script::provider::XScriptProviderSupplier > xSPS( m_xModel, uno::UNO_QUERY );
+ uno::Reference< script::provider::XScriptProvider > xScriptProvider;
+ if ( xSPS.is() )
+ {
+ xScriptProvider = xSPS->getScriptProvider();
+ }
+ if ( !(xScriptProvider.is() && mpShell) )
+ return;
+
+ BasicManager* pBasicManager = mpShell->GetBasicManager();
+ OUString sProject;
+ OUString sScriptCode( evt.ScriptCode );
+ // dialogs pass their own library, presence of Dot determines that
+ if ( sScriptCode.indexOf( '.' ) == -1 )
+ {
+ //'Project' is a better default but I want to force failures
+ //OUString sMacroLoc("Project");
+ sProject = "Standard";
+
+ if (!pBasicManager->GetName().isEmpty())
+ {
+ sProject = pBasicManager->GetName();
+ }
+ }
+ else
+ {
+ sal_Int32 nIndex = sScriptCode.indexOf( '.' );
+ sProject = sScriptCode.copy( 0, nIndex );
+ sScriptCode = sScriptCode.copy( nIndex + 1 );
+ }
+ OUString sMacroLoc = sProject + "." + sScriptCode + ".";
+
+ for (const auto& rTxInfo : eventInfo_it->second)
+ {
+ // If the document is closed, we should not execute macro.
+ if (m_bDocClosed)
+ {
+ break;
+ }
+
+ // see if we have a match for the handlerextension
+ // where ScriptCode is methodname_handlerextension
+ OUString sToResolve = sMacroLoc + sName + rTxInfo.sVBAName;
+
+ ooo::vba::MacroResolvedInfo aMacroResolvedInfo = ooo::vba::resolveVBAMacro( mpShell, sToResolve );
+ if ( aMacroResolvedInfo.mbFound )
+ {
+
+ if (! rTxInfo.ApproveRule(evt, rTxInfo.pPara) )
+ {
+ continue;
+ }
+
+ // !! translate arguments & emulate events where necessary
+ Sequence< Any > aArguments;
+ if ( rTxInfo.toVBA )
+ {
+ aArguments = rTxInfo.toVBA( evt.Arguments );
+ }
+ else
+ {
+ aArguments = evt.Arguments;
+ }
+ if ( aArguments.hasElements() )
+ {
+ // call basic event handlers for event
+
+ // create script url
+ OUString url = aMacroResolvedInfo.msResolvedMacro;
+ try
+ {
+ uno::Any aDummyCaller( OUString("Error") );
+ if ( pRet )
+ {
+ ooo::vba::executeMacro( mpShell, url, aArguments, *pRet, aDummyCaller );
+ }
+ else
+ {
+ uno::Any aRet;
+ ooo::vba::executeMacro( mpShell, url, aArguments, aRet, aDummyCaller );
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("scripting", "event script raised" );
+ }
+ }
+ }
+ }
+}
+
+namespace {
+
+class VBAToOOEventDescGen : public ::cppu::WeakImplHelper< XVBAToOOEventDescGen, css::lang::XServiceInfo >
+{
+public:
+ VBAToOOEventDescGen();
+
+ // XVBAToOOEventDescGen
+ virtual Sequence< ScriptEventDescriptor > SAL_CALL getEventDescriptions( const OUString& sCtrlServiceName, const OUString& sCodeName ) override;
+ virtual Reference< XScriptEventsSupplier > SAL_CALL getEventSupplier( const Reference< XInterface >& xControl, const OUString& sCodeName ) override;
+
+ OUString SAL_CALL getImplementationName() override
+ {
+ return "ooo.vba.VBAToOOEventDesc";
+ }
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ {
+ return cppu::supportsService(this, ServiceName);
+ }
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return { getImplementationName() };
+ }
+
+};
+
+}
+
+VBAToOOEventDescGen::VBAToOOEventDescGen() {}
+
+Sequence< ScriptEventDescriptor > SAL_CALL
+VBAToOOEventDescGen::getEventDescriptions( const OUString& sCntrlServiceName, const OUString& sCodeName )
+{
+ ScriptEventHelper evntHelper( sCntrlServiceName );
+ return evntHelper.createEvents( sCodeName );
+}
+
+Reference< XScriptEventsSupplier > SAL_CALL
+VBAToOOEventDescGen::getEventSupplier( const Reference< XInterface >& xControl, const OUString& sCodeName )
+{
+ ScriptEventHelper evntHelper( xControl );
+ Reference< XScriptEventsSupplier > xSupplier =
+ new ReadOnlyEventsSupplier(
+ evntHelper.getEventListeners(), sCodeName ) ;
+ return xSupplier;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ooo_vba_EventListener_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new EventListener);
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+ooo_vba_VBAToOOEventDesc_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new VBAToOOEventDescGen);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/scripting/source/vbaevents/vbaevents.component b/scripting/source/vbaevents/vbaevents.component
new file mode 100644
index 0000000000..75fcf02309
--- /dev/null
+++ b/scripting/source/vbaevents/vbaevents.component
@@ -0,0 +1,30 @@
+<?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="ooo.vba.EventListener"
+ constructor="ooo_vba_EventListener_get_implementation">
+ <service name="ooo.vba.EventListener"/>
+ </implementation>
+ <implementation name="ooo.vba.VBAToOOEventDesc"
+ constructor="ooo_vba_VBAToOOEventDesc_get_implementation">
+ <service name="ooo.vba.VBAToOOEventDesc"/>
+ </implementation>
+</component>