/* -*- 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 "dlgevtatt.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; // component helper namespace namespace comp_DialogModelProvider { OUString _getImplementationName() { return "com.sun.star.comp.scripting.DialogModelProvider"; } uno::Sequence< OUString > _getSupportedServiceNames() { uno::Sequence< OUString > s { "com.sun.star.awt.UnoControlDialogModelProvider" }; return s; } static uno::Reference< uno::XInterface > _create(const uno::Reference< uno::XComponentContext > & context) { return static_cast< ::cppu::OWeakObject * >(new dlgprov::DialogModelProvider(context)); } } // closing component helper namespace namespace dlgprov { Reference< resource::XStringResourceManager > lcl_getStringResourceManager(const Reference< XComponentContext >& i_xContext,const OUString& 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(); Sequence aArgs( 6 ); aArgs[0] <<= aDlgLocation; aArgs[1] <<= true; // bReadOnly aArgs[2] <<= aLocale; aArgs[3] <<= aDlgName; aArgs[4] <<= OUString(); Reference< task::XInteractionHandler > xDummyHandler; aArgs[5] <<= 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; } // component operations static OUString getImplementationName_DialogProviderImpl() { return "com.sun.star.comp.scripting.DialogProvider"; } static Sequence< OUString > getSupportedServiceNames_DialogProviderImpl() { return { "com.sun.star.awt.DialogProvider", "com.sun.star.awt.DialogProvider2", "com.sun.star.awt.ContainerWindowProvider" }; } // 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() ) { ::comphelper::NamedValueCollection aModelArgs( xModel->getArgs() ); sDocURL = aModelArgs.getOrDefault( "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 = 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 getImplementationName_DialogProviderImpl(); } sal_Bool DialogProviderImpl::supportsService( const OUString& rServiceName ) { return cppu::supportsService(this, rServiceName); } Sequence< OUString > DialogProviderImpl::getSupportedServiceNames( ) { return getSupportedServiceNames_DialogProviderImpl(); } // 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 static const char aDecorationPropName[] = "Decoration"; 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, makeAny( true ) ); xDlgModPropSet->setPropertyValue( "Title", makeAny( 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 static Reference< XInterface > create_DialogProviderImpl( Reference< XComponentContext > const & xContext ) { return static_cast< lang::XTypeProvider * >( new DialogProviderImpl( xContext ) ); } static struct ::cppu::ImplementationEntry const s_component_entries [] = { {create_DialogProviderImpl, getImplementationName_DialogProviderImpl,getSupportedServiceNames_DialogProviderImpl, ::cppu::createSingleComponentFactory,nullptr, 0}, { &comp_DialogModelProvider::_create,&comp_DialogModelProvider::_getImplementationName,&comp_DialogModelProvider::_getSupportedServiceNames,&::cppu::createSingleComponentFactory, nullptr, 0 }, { nullptr, nullptr, nullptr, nullptr, nullptr, 0 } }; } // namespace dlgprov // component exports extern "C" { SAL_DLLPUBLIC_EXPORT void * dlgprov_component_getFactory( const char * pImplName, void * pServiceManager, void * pRegistryKey ) { return ::cppu::component_getFactoryHelper( pImplName, pServiceManager, pRegistryKey, ::dlgprov::s_component_entries ); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */