/* -*- 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 "documentdefinition.hxx"
#include "databasedocument.hxx"
#include <ModelImpl.hxx>
#include <stringconstants.hxx>
#include <sdbcoretools.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <osl/diagnose.h>
#include <comphelper/compbase.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/classids.hxx>
#include <comphelper/propertysequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/types.hxx>
#include <com/sun/star/frame/XUntitledNumbers.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/frame/XTitle.hpp>
#include <com/sun/star/frame/XController.hpp>
#include <com/sun/star/task/XJobExecutor.hpp>
#include <com/sun/star/report/XReportDefinition.hpp>
#include <com/sun/star/report/XReportEngine.hpp>
#include <com/sun/star/ucb/OpenMode.hpp>
#include <com/sun/star/embed/WrongStateException.hpp>
#include <com/sun/star/embed/EmbeddedObjectCreator.hpp>
#include <com/sun/star/embed/Aspects.hpp>
#include <com/sun/star/embed/OOoEmbeddedObjectFactory.hpp>
#include <ucbhelper/cancelcommandexecution.hxx>
#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
#include <com/sun/star/embed/ElementModes.hpp>
#include <com/sun/star/embed/XEmbedPersist.hpp>
#include <com/sun/star/embed/EmbedStates.hpp>
#include <com/sun/star/embed/EntryInitModes.hpp>
#include <com/sun/star/ucb/MissingPropertiesException.hpp>
#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
#include <com/sun/star/util/CloseVetoException.hpp>
#include <com/sun/star/frame/XModule.hpp>
#include <com/sun/star/datatransfer/DataFlavor.hpp>
#include <com/sun/star/datatransfer/XTransferable.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/embed/XCommonEmbedPersist.hpp>
#include "intercept.hxx"
#include <com/sun/star/sdb/ErrorCondition.hpp>
#include <com/sun/star/sdb/XInteractionDocumentSave.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/sdb/DocumentSaveRequest.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/MacroExecMode.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/form/XFormsSupplier.hpp>
#include <com/sun/star/form/XForm.hpp>
#include <comphelper/interaction.hxx>
#include <connectivity/dbtools.hxx>
#include <vcl/svapp.hxx>
#include <osl/mutex.hxx>
#include <com/sun/star/view/XViewSettingsSupplier.hpp>
#include <core_resource.hxx>
#include <strings.hrc>
#include <strings.hxx>
#include <com/sun/star/task/XInteractionApprove.hpp>
#include <com/sun/star/task/XInteractionDisapprove.hpp>
#include <com/sun/star/frame/XLayoutManager.hpp>
#include <cppuhelper/compbase.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/implbase.hxx>
#include <comphelper/mimeconfighelper.hxx>
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
#include <com/sun/star/io/WrongFormatException.hpp>
#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp>
#include <com/sun/star/sdb/application/DatabaseObject.hpp>
#include <com/sun/star/util/XModifiable2.hpp>

using namespace ::com::sun::star;
using namespace view;
using namespace uno;
using namespace util;
using namespace ucb;
using namespace beans;
using namespace lang;
using namespace awt;
using namespace embed;
using namespace frame;
using namespace document;
using namespace sdbc;
using namespace sdb;
using namespace io;
using namespace container;
using namespace datatransfer;
using namespace task;
using namespace form;
using namespace drawing;
using namespace ::osl;
using namespace ::comphelper;
using namespace ::cppu;

using sdb::application::XDatabaseDocumentUI;
namespace DatabaseObject = sdb::application::DatabaseObject;

#define DEFAULT_WIDTH  10000
#define DEFAULT_HEIGHT  7500

namespace dbaccess
{

    typedef ::std::optional< bool > optional_bool;

    // helper
    namespace
    {
        OUString lcl_determineContentType_nothrow( const Reference< XStorage >& _rxContainerStorage,
            const OUString& _rEntityName )
        {
            OUString sContentType;
            try
            {
                ::utl::SharedUNOComponent< XPropertySet > xStorageProps(
                    _rxContainerStorage->openStorageElement( _rEntityName, ElementModes::READ ), UNO_QUERY_THROW );
                OSL_VERIFY( xStorageProps->getPropertyValue( INFO_MEDIATYPE ) >>= sContentType );
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }
            return sContentType;
        }
    }

    // OEmbedObjectHolder
    typedef ::comphelper::WeakComponentImplHelper<   embed::XStateChangeListener > TEmbedObjectHolder;

    class OEmbedObjectHolder : public TEmbedObjectHolder
    {
        Reference< XEmbeddedObject >    m_xBroadCaster;
        ODocumentDefinition*            m_pDefinition;
        bool                            m_bInStateChange;
    protected:
        virtual void disposing(std::unique_lock<std::mutex>& rGuard) override;
    public:
        OEmbedObjectHolder(const Reference< XEmbeddedObject >& _xBroadCaster,ODocumentDefinition* _pDefinition)
            : m_xBroadCaster(_xBroadCaster)
            ,m_pDefinition(_pDefinition)
            ,m_bInStateChange(false)
        {
            osl_atomic_increment( &m_refCount );
            {
                if ( m_xBroadCaster.is() )
                    m_xBroadCaster->addStateChangeListener(this);
            }
            osl_atomic_decrement( &m_refCount );
        }

        virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
        virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
        virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;
    };

    void OEmbedObjectHolder::disposing(std::unique_lock<std::mutex>& /*rGuard*/)
    {
        if ( m_xBroadCaster.is() )
            m_xBroadCaster->removeStateChangeListener(this);
        m_xBroadCaster = nullptr;
        m_pDefinition = nullptr;
    }

    void SAL_CALL OEmbedObjectHolder::changingState( const lang::EventObject& /*aEvent*/, ::sal_Int32 /*nOldState*/, ::sal_Int32 /*nNewState*/ )
    {
    }

    void SAL_CALL OEmbedObjectHolder::stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState )
    {
        if ( !m_bInStateChange && nNewState == EmbedStates::RUNNING && nOldState == EmbedStates::ACTIVE && m_pDefinition )
        {
            m_bInStateChange = true;
            Reference<XInterface> xHoldAlive(static_cast< ::cppu::OWeakObject* >(m_pDefinition),UNO_QUERY);
            {
                Reference<XEmbeddedObject> xEmbeddedObject(aEvent.Source,UNO_QUERY);
                if ( xEmbeddedObject.is() )
                    xEmbeddedObject->changeState(EmbedStates::LOADED);
            }
            m_bInStateChange = false;
        }
    }

    void SAL_CALL OEmbedObjectHolder::disposing( const lang::EventObject& /*Source*/ )
    {
        m_xBroadCaster = nullptr;
    }

    // OEmbeddedClientHelper
    class OEmbeddedClientHelper : public ::cppu::WeakImplHelper<XEmbeddedClient>
    {
    public:
        virtual void SAL_CALL saveObject(  ) override
        {
        }
        // XComponentSupplier
        virtual Reference< util::XCloseable > SAL_CALL getComponent(  ) override
        {
            return Reference< css::util::XCloseable >();
        }

        // XEmbeddedClient
        virtual void SAL_CALL visibilityChanged( sal_Bool /*bVisible*/ ) override
        {
        }
    };

    namespace {

    // LockModifiable
    class LockModifiable
    {
    public:
        explicit LockModifiable( const Reference< XInterface >& i_rModifiable )
            :m_xModifiable( i_rModifiable, UNO_QUERY )
        {
            OSL_ENSURE( m_xModifiable.is(), "LockModifiable::LockModifiable: invalid component!" );
            if ( m_xModifiable.is() )
            {
                if ( !m_xModifiable->isSetModifiedEnabled() )
                {
                    // somebody already locked that, no need to lock it, again, and no need to unlock it later
                    m_xModifiable.clear();
                }
                else
                {
                    m_xModifiable->disableSetModified();
                }
            }
        }

        ~LockModifiable()
        {
            if ( m_xModifiable.is() )
                m_xModifiable->enableSetModified();
        }

    private:
        Reference< XModifiable2 >   m_xModifiable;
    };

    }

    // LifetimeCoupler
    typedef ::cppu::WeakImplHelper<   css::lang::XEventListener
                                  >   LifetimeCoupler_Base;

    namespace {

    /** helper class which couples the lifetime of a component to the lifetime
        of another component

        Instances of this class are constructed with two components. The first is
        simply held by reference, and thus kept alive. The second one is observed
        for <code>disposing</code> calls - if they occur, i.e. if the component dies,
        the reference to the first component is cleared.

        This way, you can ensure that a certain component is kept alive as long
        as a second component is not disposed.
    */
    class LifetimeCoupler : public LifetimeCoupler_Base
    {
    private:
        Reference< XInterface > m_xClient;

    public:
        static void couple( const Reference< XInterface >& _rxClient, const Reference< XComponent >& _rxActor )
        {
            new LifetimeCoupler( _rxClient, _rxActor );
        }

    private:
        LifetimeCoupler( const Reference< XInterface >& _rxClient, const Reference< XComponent >& _rxActor )
            :m_xClient( _rxClient )
        {
            OSL_ENSURE( _rxActor.is(), "LifetimeCoupler::LifetimeCoupler: this will crash!" );
            osl_atomic_increment( &m_refCount );
            {
                _rxActor->addEventListener( this );
            }
            osl_atomic_decrement( &m_refCount );
            OSL_ENSURE( m_refCount, "LifetimeCoupler::LifetimeCoupler: the actor is not holding us by hard ref - this won't work!" );
        }

        virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
    protected:
    };

    }

    void SAL_CALL LifetimeCoupler::disposing( const css::lang::EventObject& /*Source*/ )
    {
        m_xClient.clear();
    }

    namespace {

    // ODocumentSaveContinuation
    class ODocumentSaveContinuation : public OInteraction< XInteractionDocumentSave >
    {
        OUString     m_sName;
        Reference<XContent> m_xParentContainer;

    public:
        ODocumentSaveContinuation() { }

        const Reference<XContent>& getContent() const { return m_xParentContainer; }
        const OUString&       getName() const { return m_sName; }

        // XInteractionDocumentSave
        virtual void SAL_CALL setName( const OUString& _sName,const Reference<XContent>& _xParent) override;
    };

    }

    void SAL_CALL ODocumentSaveContinuation::setName( const OUString& _sName,const Reference<XContent>& _xParent)
    {
        m_sName = _sName;
        m_xParentContainer = _xParent;
    }

OUString ODocumentDefinition::GetDocumentServiceFromMediaType( const Reference< XStorage >& _rxContainerStorage,
    const OUString& _rEntityName, const Reference< XComponentContext >& _rContext,
    Sequence< sal_Int8 >& _rClassId )
{
    return GetDocumentServiceFromMediaType(
        lcl_determineContentType_nothrow( _rxContainerStorage, _rEntityName ),
        _rContext, _rClassId );
}

OUString ODocumentDefinition::GetDocumentServiceFromMediaType( const OUString& _rMediaType,
    const Reference< XComponentContext >& _rContext, Sequence< sal_Int8 >& _rClassId )
{
    OUString sResult;
    try
    {
        ::comphelper::MimeConfigurationHelper aConfigHelper( _rContext );
        sResult = aConfigHelper.GetDocServiceNameFromMediaType( _rMediaType );
        _rClassId = comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation(aConfigHelper.GetExplicitlyRegisteredObjClassID( _rMediaType ));
        if ( !_rClassId.hasElements() && !sResult.isEmpty() )
        {
            Reference< XNameAccess > xObjConfig = aConfigHelper.GetObjConfiguration();
            if ( xObjConfig.is() )
            {
                const Sequence< OUString > aClassIDs = xObjConfig->getElementNames();
                for ( OUString const & classId : aClassIDs )
                {
                    Reference< XNameAccess > xObjectProps;
                    OUString aEntryDocName;

                    if (    ( xObjConfig->getByName( classId ) >>= xObjectProps ) && xObjectProps.is()
                         && ( xObjectProps->getByName(u"ObjectDocumentServiceName"_ustr) >>= aEntryDocName )
                         && aEntryDocName == sResult )
                    {
                        _rClassId = comphelper::MimeConfigurationHelper::GetSequenceClassIDRepresentation(classId);
                        break;
                    }
                }
            }
        }
#if OSL_DEBUG_LEVEL > 0
        // alternative, shorter approach
        const Sequence< NamedValue > aProps( aConfigHelper.GetObjectPropsByMediaType( _rMediaType ) );
        const ::comphelper::NamedValueCollection aMediaTypeProps( aProps );
        const OUString sAlternativeResult = aMediaTypeProps.getOrDefault( u"ObjectDocumentServiceName"_ustr, OUString() );
        OSL_ENSURE( sAlternativeResult == sResult, "ODocumentDefinition::GetDocumentServiceFromMediaType: failed, this approach is *not* equivalent (1)!" );
        const Sequence< sal_Int8 > aAlternativeClassID = aMediaTypeProps.getOrDefault( u"ClassID"_ustr, Sequence< sal_Int8 >() );
        OSL_ENSURE( aAlternativeClassID == _rClassId, "ODocumentDefinition::GetDocumentServiceFromMediaType: failed, this approach is *not* equivalent (2)!" );
#endif
    }
    catch ( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
    return sResult;
}

// ODocumentDefinition

ODocumentDefinition::ODocumentDefinition( const Reference< XInterface >& _rxContainer, const Reference< XComponentContext >& _xORB,
                                          const TContentPtr& _pImpl, bool _bForm )
    :OContentHelper(_xORB,_rxContainer,_pImpl)
    ,OPropertyStateContainer(OContentHelper::rBHelper)
    ,m_bForm(_bForm)
    ,m_bOpenInDesign(false)
    ,m_bInExecute(false)
    ,m_bRemoveListener(false)
{
    registerProperties();
}

void ODocumentDefinition::initialLoad( const Sequence< sal_Int8 >& i_rClassID, const Sequence< PropertyValue >& i_rCreationArgs,
                                       const Reference< XConnection >& i_rConnection )
{
    OSL_ENSURE( i_rClassID.hasElements(), "ODocumentDefinition::initialLoad: illegal class ID!" );
    if ( !i_rClassID.hasElements() )
        return;

    loadEmbeddedObject( i_rConnection, i_rClassID, i_rCreationArgs, false, false );
}

ODocumentDefinition::~ODocumentDefinition()
{
    if ( !OContentHelper::rBHelper.bInDispose && !OContentHelper::rBHelper.bDisposed )
    {
        acquire();
        dispose();
    }

    if ( m_pInterceptor.is() )
    {
        m_pInterceptor->dispose();
        m_pInterceptor.clear();
    }
}

void ODocumentDefinition::closeObject()
{
    ::osl::MutexGuard aGuard(m_aMutex);
    if ( m_xEmbeddedObject.is() )
    {
        try
        {
            m_xEmbeddedObject->close(true);
        }
        catch(const Exception&)
        {
        }
        m_xEmbeddedObject = nullptr;
        m_pClientHelper.clear();
    }
}

void SAL_CALL ODocumentDefinition::disposing()
{
    OContentHelper::disposing();
    ::osl::MutexGuard aGuard(m_aMutex);
    closeObject();
    ::comphelper::disposeComponent(m_xListener);
    if ( m_bRemoveListener )
    {
        rtl::Reference<ODatabaseDocument> xCloseable(m_pImpl->m_pDataSource->getModel_noCreate());
        if ( xCloseable.is() )
            xCloseable->removeCloseListener(this);
    }
}

css::uno::Sequence<sal_Int8> ODocumentDefinition::getImplementationId()
{
    return css::uno::Sequence<sal_Int8>();
}

css::uno::Sequence< css::uno::Type > ODocumentDefinition::getTypes()
{
    return  ::comphelper::concatSequences(
        OContentHelper::getTypes( ),
        OPropertyStateContainer::getTypes( ),
        ODocumentDefinition_Base::getTypes( )
    );
}
IMPLEMENT_FORWARD_XINTERFACE3( ODocumentDefinition,OContentHelper,OPropertyStateContainer,ODocumentDefinition_Base)

void ODocumentDefinition::registerProperties()
{
    registerProperty(PROPERTY_NAME, PROPERTY_ID_NAME, PropertyAttribute::CONSTRAINED | PropertyAttribute::BOUND | PropertyAttribute::READONLY,
                     &m_pImpl->m_aProps.aTitle, cppu::UnoType<decltype(m_pImpl->m_aProps.aTitle)>::get());

    registerProperty(PROPERTY_AS_TEMPLATE, PROPERTY_ID_AS_TEMPLATE, PropertyAttribute::READONLY, &m_pImpl->m_aProps.bAsTemplate,
                     cppu::UnoType<decltype(m_pImpl->m_aProps.bAsTemplate)>::get());

    registerProperty(PROPERTY_PERSISTENT_NAME, PROPERTY_ID_PERSISTENT_NAME, PropertyAttribute::READONLY, &m_pImpl->m_aProps.sPersistentName,
                     cppu::UnoType<decltype(m_pImpl->m_aProps.sPersistentName)>::get());

    registerProperty(PROPERTY_IS_FORM, PROPERTY_ID_IS_FORM, PropertyAttribute::READONLY, &m_bForm, cppu::UnoType<decltype(m_bForm)>::get());
}

void SAL_CALL ODocumentDefinition::getFastPropertyValue( Any& o_rValue, sal_Int32 i_nHandle ) const
{
    if ( i_nHandle == PROPERTY_ID_PERSISTENT_PATH )
    {
        OUString sPersistentPath;
        if ( !m_pImpl->m_aProps.sPersistentName.isEmpty() )
        {
            sPersistentPath = ODatabaseModelImpl::getObjectContainerStorageName( m_bForm ? ODatabaseModelImpl::ObjectType::Form : ODatabaseModelImpl::ObjectType::Report )
                + "/" + m_pImpl->m_aProps.sPersistentName;
        }
        o_rValue <<= sPersistentPath;
        return;
    }

    OPropertyStateContainer::getFastPropertyValue( o_rValue, i_nHandle );
}

Reference< XPropertySetInfo > SAL_CALL ODocumentDefinition::getPropertySetInfo(  )
{
    Reference<XPropertySetInfo> xInfo( createPropertySetInfo( getInfoHelper() ) );
    return xInfo;
}

IPropertyArrayHelper& ODocumentDefinition::getInfoHelper()
{
    return *getArrayHelper();
}

IPropertyArrayHelper* ODocumentDefinition::createArrayHelper( ) const
{
    // properties maintained by our base class (see registerProperties)
    Sequence< Property > aProps;
    describeProperties( aProps );

    // properties not maintained by our base class
    Sequence< Property > aManualProps{ { /* Name       */ PROPERTY_PERSISTENT_PATH,
                                         /* Handle     */ PROPERTY_ID_PERSISTENT_PATH,
                                         /* Type       */ ::cppu::UnoType<OUString>::get(),
                                         /* Attributes */ PropertyAttribute::READONLY } };

    return new OPropertyArrayHelper( ::comphelper::concatSequences( aProps, aManualProps ) );
}

namespace {

class OExecuteImpl
{
    bool& m_rbSet;
public:
    explicit OExecuteImpl(bool& _rbSet) : m_rbSet(_rbSet){ m_rbSet=true; }
    ~OExecuteImpl(){ m_rbSet = false; }
};

    bool lcl_extractOpenMode( const Any& _rValue, sal_Int32& _out_rMode )
    {
        OpenCommandArgument aOpenCommand;
        if ( _rValue >>= aOpenCommand )
            _out_rMode = aOpenCommand.Mode;
        else
        {
            OpenCommandArgument2 aOpenCommand2;
            if ( _rValue >>= aOpenCommand2 )
                _out_rMode = aOpenCommand2.Mode;
            else
                return false;
        }
        return true;
    }
}

void ODocumentDefinition::impl_removeFrameFromDesktop_throw( const Reference<XComponentContext> & _rxContext, const Reference< XFrame >& _rxFrame )
{
    Reference< XDesktop2 > xDesktop = Desktop::create( _rxContext );
    Reference< XFrames > xFrames( xDesktop->getFrames(), UNO_SET_THROW );
    xFrames->remove( _rxFrame );
}

void ODocumentDefinition::impl_onActivateEmbeddedObject_nothrow( const bool i_bReactivated )
{
    try
    {
        Reference< XModel > xModel( getComponent(), UNO_QUERY );
        Reference< XController > xController( xModel.is() ? xModel->getCurrentController() : Reference< XController >() );
        if ( !xController.is() )
            return;

        if ( !m_xListener.is() )
            // it's the first time the embedded object has been activated
            // create an OEmbedObjectHolder
            m_xListener = new OEmbedObjectHolder( m_xEmbeddedObject, this );

        // raise the window to top (especially necessary if this is not the first activation)
        Reference< XFrame > xFrame( xController->getFrame(), UNO_SET_THROW );
        Reference< XTopWindow > xTopWindow( xFrame->getContainerWindow(), UNO_QUERY_THROW );
        xTopWindow->toFront();

        // remove the frame from the desktop's frame collection because we need full control of it.
        impl_removeFrameFromDesktop_throw( m_aContext, xFrame );

        // ensure that we ourself are kept alive as long as the embedded object's frame is
        // opened
        LifetimeCoupler::couple( *this, xFrame );

        // init the edit view
        if ( m_bForm && m_bOpenInDesign && !i_bReactivated )
            impl_initFormEditView( xController );
    }
    catch( const RuntimeException& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}

namespace
{
    // PreserveVisualAreaSize
    /** stack-guard for preserving the size of the VisArea of an XModel
    */
    class PreserveVisualAreaSize
    {
    private:
        Reference< XVisualObject >  m_xVisObject;
        awt::Size m_aOriginalSize;

    public:
        explicit PreserveVisualAreaSize( const Reference< XModel >& _rxModel )
            :m_xVisObject( _rxModel, UNO_QUERY )
        {
            if ( m_xVisObject.is() )
            {
                try
                {
                    m_aOriginalSize = m_xVisObject->getVisualAreaSize( Aspects::MSOLE_CONTENT );
                }
                catch ( const Exception& )
                {
                    TOOLS_WARN_EXCEPTION( "dbaccess", "PreserveVisualAreaSize::PreserveVisualAreaSize" );
                }
            }
        }

        ~PreserveVisualAreaSize()
        {
            if ( m_xVisObject.is() && m_aOriginalSize.Width && m_aOriginalSize.Height )
            {
                try
                {
                    m_xVisObject->setVisualAreaSize( Aspects::MSOLE_CONTENT, m_aOriginalSize );
                }
                catch ( const Exception& )
                {
                    TOOLS_WARN_EXCEPTION( "dbaccess", "PreserveVisualAreaSize::~PreserveVisualAreaSize" );
                }
            }
        }
    };

    // LayoutManagerLock
    /** helper class for stack-usage which during its lifetime locks a layout manager
    */
    class LayoutManagerLock
    {
    private:
        Reference< XLayoutManager > m_xLayoutManager;

    public:
        explicit LayoutManagerLock( const Reference< XController >& _rxController )
        {
            OSL_ENSURE( _rxController.is(), "LayoutManagerLock::LayoutManagerLock: this will crash!" );
            Reference< XFrame > xFrame( _rxController->getFrame() );
            try
            {
                Reference< XPropertySet > xPropSet( xFrame, UNO_QUERY_THROW );
                m_xLayoutManager.set(
                    xPropSet->getPropertyValue( u"LayoutManager"_ustr ),
                    UNO_QUERY_THROW );
                m_xLayoutManager->lock();

            }
            catch( const Exception& )
            {
                TOOLS_WARN_EXCEPTION( "dbaccess", "LayoutManagerLock::LayoutManagerLock" );
            }
        }

        ~LayoutManagerLock()
        {
            try
            {
                // unlock the layout manager
                if ( m_xLayoutManager.is() )
                    m_xLayoutManager->unlock();
            }
            catch( const Exception& )
            {
                TOOLS_WARN_EXCEPTION( "dbaccess", "LayoutManagerLock::~LayoutManagerLock" );
            }
        }
    };
}

void ODocumentDefinition::impl_initFormEditView( const Reference< XController >& _rxController )
{
    try
    {
        Reference< XViewSettingsSupplier > xSettingsSupplier( _rxController, UNO_QUERY_THROW );
        Reference< XPropertySet > xViewSettings( xSettingsSupplier->getViewSettings(), UNO_SET_THROW );

        // the below code could indirectly tamper with the "modified" flag of the model, temporarily disable this
        LockModifiable aLockModify( _rxController->getModel() );

        // The visual area size can be changed by the setting of the following properties
        // so it should be restored later
        PreserveVisualAreaSize aPreserveVisAreaSize( _rxController->getModel() );

        // Layout manager should not layout while the size is still not restored
        // so it will stay locked for this time
        LayoutManagerLock aLockLayout( _rxController );

        // setting of the visual properties
        xViewSettings->setPropertyValue(u"ShowRulers"_ustr,Any(true));
        xViewSettings->setPropertyValue(u"ShowVertRuler"_ustr,Any(true));
        xViewSettings->setPropertyValue(u"ShowHoriRuler"_ustr,Any(true));
        xViewSettings->setPropertyValue(u"IsRasterVisible"_ustr,Any(true));
        xViewSettings->setPropertyValue(u"IsSnapToRaster"_ustr,Any(true));
        xViewSettings->setPropertyValue(u"ShowOnlineLayout"_ustr,Any(true));
        xViewSettings->setPropertyValue(u"RasterSubdivisionX"_ustr,Any(sal_Int32(5)));
        xViewSettings->setPropertyValue(u"RasterSubdivisionY"_ustr,Any(sal_Int32(5)));
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}

void ODocumentDefinition::impl_showOrHideComponent_throw( const bool i_bShow )
{
    const sal_Int32 nCurrentState = m_xEmbeddedObject.is() ? m_xEmbeddedObject->getCurrentState() : EmbedStates::LOADED;
    switch ( nCurrentState )
    {
    default:
    case EmbedStates::LOADED:
        throw embed::WrongStateException( OUString(), *this );

    case EmbedStates::RUNNING:
        if ( !i_bShow )
            // fine, a running (and not yet active) object is never visible
            return;
        {
            LockModifiable aLockModify( impl_getComponent_throw() );
            m_xEmbeddedObject->changeState( EmbedStates::ACTIVE );
            impl_onActivateEmbeddedObject_nothrow( false );
        }
        break;

    case EmbedStates::ACTIVE:
    {
        Reference< XModel > xEmbeddedDoc( impl_getComponent_throw(), UNO_QUERY_THROW );
        Reference< XController > xEmbeddedController( xEmbeddedDoc->getCurrentController(), UNO_SET_THROW );
        Reference< XFrame > xEmbeddedFrame( xEmbeddedController->getFrame(), UNO_SET_THROW );
        Reference< XWindow > xEmbeddedWindow( xEmbeddedFrame->getContainerWindow(), UNO_SET_THROW );
        xEmbeddedWindow->setVisible( i_bShow );
    }
    break;
    }
}

Any ODocumentDefinition::onCommandOpenSomething( const Any& _rOpenArgument, const bool _bActivate,
        const Reference< XCommandEnvironment >& _rxEnvironment )
{
    OExecuteImpl aExecuteGuard( m_bInExecute );

    Reference< XConnection > xConnection;
    sal_Int32 nOpenMode = OpenMode::DOCUMENT;

    ::comphelper::NamedValueCollection aDocumentArgs;

    // for the document, default to the interaction handler as used for loading the DB doc
    // This might be overwritten below, when examining _rOpenArgument.
    const ::comphelper::NamedValueCollection& aDBDocArgs( m_pImpl->m_pDataSource->getMediaDescriptor() );
    Reference< XInteractionHandler > xHandler( aDBDocArgs.getOrDefault( u"InteractionHandler"_ustr, Reference< XInteractionHandler >() ) );
    if ( xHandler.is() )
        aDocumentArgs.put( u"InteractionHandler"_ustr, xHandler );

    ::std::optional< sal_Int16 > aDocumentMacroMode;

    if ( !lcl_extractOpenMode( _rOpenArgument, nOpenMode ) )
    {
        Sequence< PropertyValue > aArguments;
        if ( _rOpenArgument >>= aArguments )
        {
            for (auto& arg : aArguments)
            {
                if (arg.Name == PROPERTY_ACTIVE_CONNECTION)
                {
                    xConnection.set(arg.Value, UNO_QUERY);
                    continue;
                }

                if (lcl_extractOpenMode(arg.Value, nOpenMode))
                    continue;

                if (arg.Name == "MacroExecutionMode")
                {
                    sal_Int16 nMacroExecMode( !aDocumentMacroMode ? MacroExecMode::USE_CONFIG : *aDocumentMacroMode );
                    OSL_VERIFY(arg.Value >>= nMacroExecMode);
                    aDocumentMacroMode = nMacroExecMode;
                    continue;
                }

                // unknown argument -> pass to the loaded document
                aDocumentArgs.put(arg.Name, arg.Value);
            }
        }
    }

    bool bExecuteDBDocMacros = m_pImpl->m_pDataSource->checkMacrosOnLoading();
        // Note that this call implies the user might be asked for the macro execution mode.
        // Normally, this would happen when the database document is loaded, and subsequent calls
        // will simply use the user's decision from this point in time.
        // However, it is possible to programmatically load forms/reports, without actually
        // loading the database document into a frame. In this case, the user will be asked
        // here and now.
        // #i87741#

    // allow the command arguments to downgrade the macro execution mode, but not to upgrade
    // it
    if  (   ( m_pImpl->m_pDataSource->getImposedMacroExecMode() == MacroExecMode::USE_CONFIG )
        &&  bExecuteDBDocMacros
        )
    {
        // while loading the whole database document, USE_CONFIG, was passed.
        // Additionally, *by now* executing macros from the DB doc is allowed (this is what bExecuteDBDocMacros
        // indicates). This means either one of:
        // 1. The DB doc or one of the sub docs contained macros and
        // 1a. the user explicitly allowed executing them
        // 1b. the configuration allows executing them without asking the user
        // 2. Neither the DB doc nor the sub docs contained macros, thus macro
        //    execution was silently enabled, assuming that any macro will be a
        //    user-created macro
        //
        // The problem with this: If the to-be-opened sub document has macros embedded in
        // the content.xml (which is valid ODF, but normally not produced by OOo itself),
        // then this has not been detected while loading the database document - it would
        // be too expensive, as it effectively would require loading all forms/reports.
        //
        // So, in such a case, and with 2. above, we would silently execute those macros,
        // regardless of the global security settings - which would be a security issue, of
        // course.
        if ( m_pImpl->m_pDataSource->determineEmbeddedMacros() == ODatabaseModelImpl::EmbeddedMacros::NONE )
        {
            // this is case 2. from above
            // So, pass a USE_CONFIG to the to-be-loaded document. This means that
            // the user will be prompted with a security message upon opening this
            // sub document, in case the settings require this, *and* the document
            // contains scripts in the content.xml. But this is better than the security
            // issue we had before ...
            aDocumentMacroMode = MacroExecMode::USE_CONFIG;
        }
    }

    if ( !aDocumentMacroMode )
    {
        // nobody so far felt responsible for setting it
        // => use the DBDoc-wide macro exec mode for the document, too
        aDocumentMacroMode = bExecuteDBDocMacros ? MacroExecMode::ALWAYS_EXECUTE_NO_WARN
                                                 : MacroExecMode::NEVER_EXECUTE;
    }
    aDocumentArgs.put( u"MacroExecutionMode"_ustr, *aDocumentMacroMode );

    if  (   ( nOpenMode == OpenMode::ALL )
        ||  ( nOpenMode == OpenMode::FOLDERS )
        ||  ( nOpenMode == OpenMode::DOCUMENTS )
        ||  ( nOpenMode == OpenMode::DOCUMENT_SHARE_DENY_NONE )
        ||  ( nOpenMode == OpenMode::DOCUMENT_SHARE_DENY_WRITE )
        )
    {
        // not supported
        ucbhelper::cancelCommandExecution(
                Any( UnsupportedOpenModeException(
                                OUString(),
                                static_cast< cppu::OWeakObject * >( this ),
                                sal_Int16( nOpenMode ) ) ),
                _rxEnvironment );
        // Unreachable
        OSL_FAIL( "unreachable" );
    }

    OSL_ENSURE( !m_pImpl->m_aProps.sPersistentName.isEmpty(),
        "ODocumentDefinition::onCommandOpenSomething: no persistent name - cannot load!" );
    if ( m_pImpl->m_aProps.sPersistentName.isEmpty() )
        return Any();

    // embedded objects themself do not support the hidden flag. We implement support for
    // it by changing the STATE to RUNNING only, instead of ACTIVE.
    bool bOpenHidden = aDocumentArgs.getOrDefault( u"Hidden"_ustr, false );
    aDocumentArgs.remove( u"Hidden"_ustr );

    loadEmbeddedObject( xConnection, Sequence< sal_Int8 >(), aDocumentArgs.getPropertyValues(), false, !m_bOpenInDesign );
    OSL_ENSURE( m_xEmbeddedObject.is(), "ODocumentDefinition::onCommandOpenSomething: what's this?" );
    if ( !m_xEmbeddedObject.is() )
        return Any();

    Reference< XModel > xModel( getComponent(), UNO_QUERY );
    Reference< report::XReportDefinition > xReportDefinition(xModel,UNO_QUERY);

    Reference< XModule > xModule( xModel, UNO_QUERY );
    if ( xModule.is() )
    {
        if ( m_bForm )
            xModule->setIdentifier( u"com.sun.star.sdb.FormDesign"_ustr );
        else if ( !xReportDefinition.is() )
            xModule->setIdentifier( u"com.sun.star.text.TextDocument"_ustr );

        updateDocumentTitle();
    }

    bool bIsAliveNewStyleReport = ( !m_bOpenInDesign && xReportDefinition.is() );
    if ( bIsAliveNewStyleReport )
    {
        // we are in ReadOnly mode
        // we would like to open the Writer or Calc with the report direct, without design it.
        Reference< report::XReportEngine > xReportEngine( m_aContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.comp.report.OReportEngineJFree"_ustr, m_aContext), UNO_QUERY_THROW );

        xReportEngine->setReportDefinition(xReportDefinition);
        xReportEngine->setActiveConnection(m_xLastKnownConnection);
        if ( bOpenHidden )
            return Any( xReportEngine->createDocumentModel() );
        return Any( xReportEngine->createDocumentAlive( nullptr ) );
    }

    if ( _bActivate && !bOpenHidden )
    {
        LockModifiable aLockModify( impl_getComponent_throw() );
        m_xEmbeddedObject->changeState( EmbedStates::ACTIVE );
        impl_onActivateEmbeddedObject_nothrow( false );
    }
    else
    {
        // ensure that we ourself are kept alive as long as the document is open
        LifetimeCoupler::couple( *this, xModel );
    }

    if ( !m_bForm && m_pImpl->m_aProps.bAsTemplate && !m_bOpenInDesign )
        ODocumentDefinition::fillReportData( m_aContext, getComponent(), xConnection );

    return Any( xModel );
}

Any SAL_CALL ODocumentDefinition::execute( const Command& aCommand, sal_Int32 CommandId, const Reference< XCommandEnvironment >& Environment )
{
    Any aRet;

    bool bOpen = aCommand.Name == "open";
    bool bOpenInDesign = aCommand.Name == "openDesign";
    bool bOpenForMail = aCommand.Name == "openForMail";
    if ( bOpen || bOpenInDesign || bOpenForMail )
    {
        // opening the document involves a lot of VCL code, which is not thread-safe, but needs the SolarMutex locked.
        // Unfortunately, the DocumentDefinition, as well as the EmbeddedObject implementation, calls into VCL-dependent
        // components *without* releasing the own mutex, which is a guaranteed recipe for deadlocks.
        // We have control over this implementation here, and in modifying it to release the own mutex before calling into
        // the VCL-dependent components is not too difficult (was there, seen it).
        // However, we do /not/ have control over the EmbeddedObject implementation, and from a first look, it seems as
        // making it release the own mutex before calling SolarMutex-code is ... difficult, at least.
        // So, to be on the same side, we lock the SolarMutex here. Yes, it sucks.
        ::SolarMutexGuard aSolarGuard;
        osl::MutexGuard aGuard(m_aMutex);
        if ( m_bInExecute )
            return aRet;

        bool bActivateObject = true;
        if ( bOpenForMail )
        {
            OSL_FAIL( "ODocumentDefinition::execute: 'openForMail' should not be used anymore - use the 'Hidden' parameter instead!" );
            bActivateObject = false;
        }

        // if the object is already opened, do nothing
        if ( m_xEmbeddedObject.is() )
        {
            sal_Int32 nCurrentState = m_xEmbeddedObject->getCurrentState();
            bool bIsActive = ( nCurrentState == EmbedStates::ACTIVE );

            if ( bIsActive )
            {
                // exception: new-style reports always create a new document when "open" is executed
                Reference< report::XReportDefinition > xReportDefinition( impl_getComponent_throw( false ), UNO_QUERY );
                bool bIsAliveNewStyleReport = ( xReportDefinition.is() && ( bOpen || bOpenForMail ) );

                if ( !bIsAliveNewStyleReport )
                {
                    impl_onActivateEmbeddedObject_nothrow( true );
                    return Any( getComponent() );
                }
            }
        }

        m_bOpenInDesign = bOpenInDesign || bOpenForMail;
        return onCommandOpenSomething( aCommand.Argument, bActivateObject, Environment );
    }

    osl::MutexGuard aGuard(m_aMutex);
    if ( m_bInExecute )
        return aRet;

    if ( aCommand.Name == "copyTo" )
    {
        Sequence<Any> aIni;
        aCommand.Argument >>= aIni;
        if ( aIni.getLength() != 2 )
        {
            OSL_FAIL( "Wrong argument type!" );
            ucbhelper::cancelCommandExecution(
                Any( IllegalArgumentException(
                                    OUString(),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }
        Reference< XStorage> xDest(aIni[0],UNO_QUERY);
        OUString sPersistentName;
        aIni[1] >>= sPersistentName;
        Reference< XStorage> xStorage = getContainerStorage();

        xStorage->copyElementTo(m_pImpl->m_aProps.sPersistentName,xDest,sPersistentName);
    }
    else if ( aCommand.Name == "preview" )
    {
        onCommandPreview(aRet);
    }
    else if ( aCommand.Name == "insert" )
    {
        Sequence<Any> aIni;
        aCommand.Argument >>= aIni;
        if ( !aIni.hasElements() )
        {
            OSL_FAIL( "Wrong argument count!" );
            ucbhelper::cancelCommandExecution(
                Any( IllegalArgumentException(
                                    OUString(),
                                    static_cast< cppu::OWeakObject * >( this ),
                                    -1 ) ),
                Environment );
            // Unreachable
        }
        OUString sURL;
        aIni[0] >>= sURL;
        onCommandInsert( sURL, Environment );
    }
    else if (   aCommand.Name == "getdocumentinfo"   // compatibility
            ||  aCommand.Name == "getDocumentInfo" )
    {
        onCommandGetDocumentProperties( aRet );
    }
    else if ( aCommand.Name == "delete" )
    {
        // delete
        closeObject();
        Reference< XStorage> xStorage = getContainerStorage();
        if ( xStorage.is() )
            xStorage->removeElement(m_pImpl->m_aProps.sPersistentName);

        dispose();

    }
    else if (   aCommand.Name == "storeOwn" // compatibility
            ||  aCommand.Name == "store"
            )
    {
        impl_store_throw();
    }
    else if (   aCommand.Name == "shutdown" // compatibility
            ||  aCommand.Name == "close"
            )
    {
        aRet <<= impl_close_throw();
    }
    else if ( aCommand.Name == "show" )
    {
        impl_showOrHideComponent_throw( true );
    }
    else if ( aCommand.Name == "hide" )
    {
        impl_showOrHideComponent_throw( false );
    }
    else
    {
        aRet = OContentHelper::execute(aCommand,CommandId,Environment);
    }

    return aRet;
}

namespace
{
    void lcl_resetChildFormsToEmptyDataSource( const Reference< XIndexAccess>& _rxFormsContainer )
    {
        OSL_PRECOND( _rxFormsContainer.is(), "lcl_resetChildFormsToEmptyDataSource: illegal call!" );
        sal_Int32 count = _rxFormsContainer->getCount();
        for ( sal_Int32 i = 0; i < count; ++i )
        {
            Reference< XForm > xForm( _rxFormsContainer->getByIndex( i ), UNO_QUERY );
            if ( !xForm.is() )
                continue;

            // if the element is a form, reset its DataSourceName property to an empty string
            try
            {
                Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW );
                xFormProps->setPropertyValue( PROPERTY_DATASOURCENAME, Any( OUString() ) );
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }

            // if the element is a container itself, step down the component hierarchy
            Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
            if ( xContainer.is() )
                lcl_resetChildFormsToEmptyDataSource( xContainer );
        }
    }

    void lcl_resetFormsToEmptyDataSource( const Reference< XEmbeddedObject>& _rxEmbeddedObject )
    {
        try
        {
            Reference< XDrawPageSupplier > xSuppPage( _rxEmbeddedObject->getComponent(), UNO_QUERY_THROW );
                // if this interface does not exist, then either getComponent returned NULL,
                // or the document is a multi-page document. The latter is allowed, but currently
                // simply not handled by this code, as it would not normally happen.

            Reference< XFormsSupplier > xSuppForms( xSuppPage->getDrawPage(), UNO_QUERY_THROW );
            Reference< XIndexAccess > xForms( xSuppForms->getForms(), UNO_QUERY_THROW );
            lcl_resetChildFormsToEmptyDataSource( xForms );
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }

    }
}

void ODocumentDefinition::onCommandInsert( const OUString& _sURL, const Reference< XCommandEnvironment >& Environment )
{
    osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );

    // Check, if all required properties were set.
    if ( _sURL.isEmpty() || m_xEmbeddedObject.is() )
    {
        OSL_FAIL( "Content::onCommandInsert - property value missing!" );

        Sequence<OUString> aProps { PROPERTY_URL };
        ucbhelper::cancelCommandExecution(
            Any( MissingPropertiesException(
                                OUString(),
                                static_cast< cppu::OWeakObject * >( this ),
                                aProps ) ),
            Environment );
        // Unreachable
    }

    if ( !m_xEmbeddedObject.is() )
    {
        Reference< XStorage> xStorage = getContainerStorage();
        if ( xStorage.is() )
        {
            Reference< XEmbeddedObjectCreator> xEmbedFactory = EmbeddedObjectCreator::create(m_aContext);
            Sequence<PropertyValue> aEmpty;
            Sequence<PropertyValue> aMediaDesc{ comphelper::makePropertyValue(PROPERTY_URL,
                                                                              _sURL) };
            m_xEmbeddedObject.set(xEmbedFactory->createInstanceInitFromMediaDescriptor( xStorage
                                                                            ,m_pImpl->m_aProps.sPersistentName
                                                                            ,aMediaDesc
                                                                            ,aEmpty),UNO_QUERY);

            lcl_resetFormsToEmptyDataSource( m_xEmbeddedObject );
            // #i57669#

            Reference<XEmbedPersist> xPersist(m_xEmbeddedObject,UNO_QUERY);
            if ( xPersist.is() )
            {
                xPersist->storeOwn();
            }
            try
            {
                if ( m_xEmbeddedObject.is() )
                    m_xEmbeddedObject->close(true);
            }
            catch(const Exception&)
            {
            }
            m_xEmbeddedObject = nullptr;
        }
    }

    aGuard.clear();
}

bool ODocumentDefinition::save(bool _bApprove, const css::uno::Reference<css::awt::XTopWindow>& rDialogParent)
{
    // default handling: instantiate an interaction handler and let it handle the parameter request
    if ( !m_bOpenInDesign )
        return false;
    try
    {

        {
            ::SolarMutexGuard aSolarGuard;

            // the request
            Reference<XNameAccess> xName(m_xParentContainer,UNO_QUERY);
            DocumentSaveRequest aRequest;
            aRequest.Name = m_pImpl->m_aProps.aTitle;
            if ( aRequest.Name.isEmpty() )
            {
                if ( m_bForm )
                    aRequest.Name = DBA_RES( RID_STR_FORM );
                else
                    aRequest.Name = DBA_RES( RID_STR_REPORT );
                aRequest.Name = ::dbtools::createUniqueName(xName,aRequest.Name);
            }

            aRequest.Content.set(m_xParentContainer,UNO_QUERY);
            rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest));
            // some knittings
            // two continuations allowed: OK and Cancel
            rtl::Reference<ODocumentSaveContinuation> pDocuSave;

            if ( m_pImpl->m_aProps.aTitle.isEmpty() )
            {
                pDocuSave = new ODocumentSaveContinuation;
                pRequest->addContinuation(pDocuSave);
            }
            if ( _bApprove )
            {
                rtl::Reference<OInteraction< XInteractionApprove >> pApprove = new OInteraction< XInteractionApprove >;
                pRequest->addContinuation(pApprove);
            }

            rtl::Reference<OInteraction< XInteractionDisapprove >> pDisApprove = new OInteraction< XInteractionDisapprove >;
            pRequest->addContinuation(pDisApprove);

            rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
            pRequest->addContinuation(pAbort);

            Reference<XWindow> xDialogParent(rDialogParent, UNO_QUERY);

            // create the handler, let it handle the request
            Reference<XInteractionHandler2> xHandler(InteractionHandler::createWithParent(m_aContext, xDialogParent));
            xHandler->handle(pRequest);

            if ( pAbort->wasSelected() )
                return false;
            if  ( pDisApprove->wasSelected() )
                return true;
            if ( pDocuSave && pDocuSave->wasSelected() )
            {
                Reference<XNameContainer> xNC( pDocuSave->getContent(), UNO_QUERY_THROW );

                ::osl::ResettableMutexGuard aGuard( m_aMutex );
                NameChangeNotifier aNameChangeAndNotify( *this, pDocuSave->getName(), aGuard );
                m_pImpl->m_aProps.aTitle = pDocuSave->getName();

                Reference< XContent> xContent = this;
                xNC->insertByName(pDocuSave->getName(),Any(xContent));

                updateDocumentTitle();
            }
        }

        ::osl::MutexGuard aGuard(m_aMutex);
        Reference<XEmbedPersist> xPersist(m_xEmbeddedObject,UNO_QUERY);
        if ( xPersist.is() )
        {
            xPersist->storeOwn();
            notifyDataSourceModified();
        }
    }
    catch(const Exception&)
    {
        TOOLS_WARN_EXCEPTION( "dbaccess", "ODocumentDefinition::save: caught an Exception (tried to let the InteractionHandler handle it)!");
    }
    return true;
}

void ODocumentDefinition::saveAs()
{
    // default handling: instantiate an interaction handler and let it handle the parameter request
    if ( !m_bOpenInDesign )
        return;

    {
        osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
        if ( m_pImpl->m_aProps.aTitle.isEmpty() )
        {
            aGuard.clear();
            save(false, css::uno::Reference<css::awt::XTopWindow>()); // (sal_False) : we don't want an approve dialog
            return;
        }
    }
    try
    {
        ::SolarMutexGuard aSolarGuard;

        // the request
        DocumentSaveRequest aRequest;
        aRequest.Name = m_pImpl->m_aProps.aTitle;

        aRequest.Content.set(m_xParentContainer,UNO_QUERY);
        rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(Any(aRequest));
        // some knittings
        // two continuations allowed: OK and Cancel
        rtl::Reference<ODocumentSaveContinuation> pDocuSave = new ODocumentSaveContinuation;
        pRequest->addContinuation(pDocuSave);
        rtl::Reference<OInteraction< XInteractionDisapprove >> pDisApprove = new OInteraction< XInteractionDisapprove >;
        pRequest->addContinuation(pDisApprove);
        rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
        pRequest->addContinuation(pAbort);

        // create the handler, let it handle the request
        Reference< XInteractionHandler2 > xHandler( InteractionHandler::createWithParent(m_aContext, nullptr) );
        xHandler->handle(pRequest);

        if ( pAbort->wasSelected() )
            return;
        if  ( pDisApprove->wasSelected() )
            return;
        if ( pDocuSave->wasSelected() )
        {
            ::osl::MutexGuard aGuard(m_aMutex);
            Reference<XNameContainer> xNC(pDocuSave->getContent(),UNO_QUERY);
            if ( xNC.is() )
            {
                if ( m_pImpl->m_aProps.aTitle != pDocuSave->getName() )
                {
                    try
                    {
                        Reference< XStorage> xStorage = getContainerStorage();

                        OUString sPersistentName = ::dbtools::createUniqueName(xStorage,u"Obj"_ustr);
                        xStorage->copyElementTo(m_pImpl->m_aProps.sPersistentName,xStorage,sPersistentName);

                        OUString sOldName = m_pImpl->m_aProps.aTitle;
                        rename(pDocuSave->getName());
                        updateDocumentTitle();

                        uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence(
                        {
                            {PROPERTY_NAME, uno::Any(sOldName)}, // set as folder
                            {PROPERTY_PERSISTENT_NAME, uno::Any(sPersistentName)},
                            {PROPERTY_AS_TEMPLATE, uno::Any(m_pImpl->m_aProps.bAsTemplate)},
                        }));
                        Reference< XMultiServiceFactory > xORB( m_xParentContainer, UNO_QUERY_THROW );
                        Reference< XInterface > xComponent( xORB->createInstanceWithArguments( SERVICE_SDB_DOCUMENTDEFINITION, aArguments ) );
                        Reference< XNameContainer > xNameContainer( m_xParentContainer, UNO_QUERY_THROW );
                        xNameContainer->insertByName( sOldName, Any( xComponent ) );
                    }
                    catch(const Exception&)
                    {
                        DBG_UNHANDLED_EXCEPTION("dbaccess");
                    }
                }
                Reference<XEmbedPersist> xPersist(m_xEmbeddedObject,UNO_QUERY);
                if ( xPersist.is() )
                {
                    xPersist->storeOwn();
                    notifyDataSourceModified();
                }
            }
        }
    }
    catch(const Exception&)
    {
        TOOLS_WARN_EXCEPTION( "dbaccess", "ODocumentDefinition::save: caught an Exception (tried to let the InteractionHandler handle it)!");
    }
}

namespace
{
    void    lcl_putLoadArgs( ::comphelper::NamedValueCollection& _io_rArgs, const optional_bool& _bSuppressMacros, const optional_bool& _bReadOnly )
    {
        if ( _bSuppressMacros.has_value() )
        {
            if ( *_bSuppressMacros )
            {
                // if we're to suppress macros, do exactly this
                _io_rArgs.put( u"MacroExecutionMode"_ustr, MacroExecMode::NEVER_EXECUTE );
            }
            else
            {
                // otherwise, put the setting only if not already present
                if ( !_io_rArgs.has( u"MacroExecutionMode"_ustr ) )
                {
                    _io_rArgs.put( u"MacroExecutionMode"_ustr, MacroExecMode::USE_CONFIG );
                }
            }
        }

        if ( _bReadOnly.has_value() )
            _io_rArgs.put( u"ReadOnly"_ustr, *_bReadOnly );
    }
}

namespace
{
    Reference< XFrame > lcl_getDatabaseDocumentFrame( ODatabaseModelImpl const & _rImpl )
    {
        Reference< XModel > xDatabaseDocumentModel( _rImpl.getModel_noCreate() );

        Reference< XController > xDatabaseDocumentController;
        if ( xDatabaseDocumentModel.is() )
            xDatabaseDocumentController = xDatabaseDocumentModel->getCurrentController();

        Reference< XFrame > xFrame;
        if ( xDatabaseDocumentController.is() )
            xFrame = xDatabaseDocumentController->getFrame();

        return xFrame;
    }
}

bool ODocumentDefinition::objectSupportsEmbeddedScripts() const
{
    bool bAllowDocumentMacros = !m_pImpl->m_pDataSource
                            ||  ( m_pImpl->m_pDataSource->determineEmbeddedMacros() == ODatabaseModelImpl::EmbeddedMacros::SubDocument );

    // if *any* of the objects of the database document already has macros, we
    // continue to allow it to have them, until the user does a migration.
    // If there are no macros, we don't allow them to be created.

    return bAllowDocumentMacros;
}

OUString ODocumentDefinition::determineContentType() const
{
    return lcl_determineContentType_nothrow( getContainerStorage(), m_pImpl->m_aProps.sPersistentName );
}

void ODocumentDefinition::separateOpenCommandArguments( const Sequence< PropertyValue >& i_rOpenCommandArguments,
        ::comphelper::NamedValueCollection& o_rDocumentLoadArgs, ::comphelper::NamedValueCollection& o_rEmbeddedObjectDescriptor )
{
    ::comphelper::NamedValueCollection aOpenCommandArguments( i_rOpenCommandArguments );

    static const std::u16string_view sObjectDescriptorArgs[] = { u"RecoveryStorage" };
    for (const auto& rObjectDescriptorArg : sObjectDescriptorArgs)
    {
        const OUString sObjectDescriptorArg(rObjectDescriptorArg);
        if ( aOpenCommandArguments.has( sObjectDescriptorArg ) )
        {
            o_rEmbeddedObjectDescriptor.put( sObjectDescriptorArg, aOpenCommandArguments.get( sObjectDescriptorArg ) );
            aOpenCommandArguments.remove( sObjectDescriptorArg );
        }
    }

    o_rDocumentLoadArgs.merge( aOpenCommandArguments, false );
}

Sequence< PropertyValue > ODocumentDefinition::fillLoadArgs( const Reference< XConnection>& _xConnection, const bool _bSuppressMacros, const bool _bReadOnly,
        const Sequence< PropertyValue >& i_rOpenCommandArguments, Sequence< PropertyValue >& _out_rEmbeddedObjectDescriptor )
{
    // (re-)create interceptor, and put it into the descriptor of the embedded object
    if ( m_pInterceptor.is() )
    {
        m_pInterceptor->dispose();
        m_pInterceptor.clear();
    }

    m_pInterceptor = new OInterceptor( this );
    Reference<XDispatchProviderInterceptor> xInterceptor = m_pInterceptor;

    ::comphelper::NamedValueCollection aEmbeddedDescriptor;
    aEmbeddedDescriptor.put( u"OutplaceDispatchInterceptor"_ustr, xInterceptor );

    ::comphelper::NamedValueCollection aMediaDesc;
    separateOpenCommandArguments( i_rOpenCommandArguments, aMediaDesc, aEmbeddedDescriptor );

    // create the OutplaceFrameProperties, and put them into the descriptor of the embedded object
    ::comphelper::NamedValueCollection OutplaceFrameProperties;
    OutplaceFrameProperties.put( u"TopWindow"_ustr, true );
    OutplaceFrameProperties.put( u"SupportPersistentWindowState"_ustr, true );

    Reference< XFrame > xParentFrame;
    if ( m_pImpl->m_pDataSource )
        xParentFrame = lcl_getDatabaseDocumentFrame( *m_pImpl->m_pDataSource );
    if ( !xParentFrame.is() )
    { // i87957 we need a parent frame
        Reference< XDesktop2 > xDesktop = Desktop::create( m_aContext );
        xParentFrame.set( xDesktop, UNO_QUERY_THROW );
        rtl::Reference<ODatabaseDocument> xCloseable(m_pImpl->m_pDataSource->getModel_noCreate());
        if ( xCloseable.is() )
        {
            xCloseable->addCloseListener(this);
            m_bRemoveListener = true;
        }
    }
    OSL_ENSURE( xParentFrame.is(), "ODocumentDefinition::fillLoadArgs: no parent frame!" );
    if  ( xParentFrame.is() )
        OutplaceFrameProperties.put( u"ParentFrame"_ustr, xParentFrame );

    aEmbeddedDescriptor.put( u"OutplaceFrameProperties"_ustr, OutplaceFrameProperties.getNamedValues() );

    // tell the embedded object to have (or not have) script support
    aEmbeddedDescriptor.put( u"EmbeddedScriptSupport"_ustr, objectSupportsEmbeddedScripts() );

    // tell the embedded object to not participate in the document recovery game - the DB doc will handle it
    aEmbeddedDescriptor.put( u"DocumentRecoverySupport"_ustr, false );

    // pass the descriptor of the embedded object to the caller
    aEmbeddedDescriptor >>= _out_rEmbeddedObjectDescriptor;

    // create the ComponentData, and put it into the document's media descriptor
    {
        ::comphelper::NamedValueCollection aComponentData;
        aComponentData.put( u"ActiveConnection"_ustr, _xConnection );
        aComponentData.put( u"ApplyFormDesignMode"_ustr, !_bReadOnly );
        aMediaDesc.put( u"ComponentData"_ustr, aComponentData.getPropertyValues() );
    }

    if ( !m_pImpl->m_aProps.aTitle.isEmpty() )
        aMediaDesc.put( u"DocumentTitle"_ustr, m_pImpl->m_aProps.aTitle );

    aMediaDesc.put( u"DocumentBaseURL"_ustr, m_pImpl->m_pDataSource->getURL() );

    // put the common load arguments into the document's media descriptor
    lcl_putLoadArgs( aMediaDesc, optional_bool( _bSuppressMacros ), optional_bool( _bReadOnly ) );

    return aMediaDesc.getPropertyValues();
}

void ODocumentDefinition::loadEmbeddedObject( const Reference< XConnection >& i_rConnection, const Sequence< sal_Int8 >& _aClassID,
        const Sequence< PropertyValue >& i_rOpenCommandArguments, const bool _bSuppressMacros, const bool _bReadOnly )
{
    if ( !m_xEmbeddedObject.is() )
    {
        Reference< XStorage> xStorage = getContainerStorage();
        if ( xStorage.is() )
        {
            Reference< XEmbeddedObjectCreator> xEmbedFactory = OOoEmbeddedObjectFactory::create(m_aContext);
            OUString sDocumentService;
            bool bSetSize = false;
            sal_Int32 nEntryConnectionMode = EntryInitModes::DEFAULT_INIT;
            Sequence< sal_Int8 > aClassID = _aClassID;
            if ( aClassID.hasElements() )
            {
                nEntryConnectionMode = EntryInitModes::TRUNCATE_INIT;
                bSetSize = true;
            }
            else
            {
                sDocumentService = GetDocumentServiceFromMediaType( getContentType(), m_aContext, aClassID );
                // check if we are not a form and
                // the org.libreoffice.report.pentaho.SOReportJobFactory is not present.
                if ( !m_bForm && sDocumentService != "com.sun.star.text.TextDocument")
                {
                    // we seem to be a "new style" report, check if report extension is present.
                    Reference< XContentEnumerationAccess > xEnumAccess( m_aContext->getServiceManager(), UNO_QUERY );
                    const OUString sReportEngineServiceName = ::dbtools::getDefaultReportEngineServiceName(m_aContext);
                    Reference< XEnumeration > xEnumDrivers = xEnumAccess->createContentEnumeration(sReportEngineServiceName);
                    if ( !xEnumDrivers.is() || !xEnumDrivers->hasMoreElements() )
                    {
                        throw css::io::WrongFormatException(DBA_RES(RID_STR_MISSING_EXTENSION));
                    }
                }
                if ( !aClassID.hasElements() )
                {
                    if ( m_bForm )
                        aClassID = MimeConfigurationHelper::GetSequenceClassID(SO3_SW_CLASSID);
                    else
                    {
                        aClassID = MimeConfigurationHelper::GetSequenceClassID(SO3_RPT_CLASSID_90);
                    }
                }
            }

            OSL_ENSURE( aClassID.hasElements(),"No Class ID" );

            Sequence< PropertyValue > aEmbeddedObjectDescriptor;
            Sequence< PropertyValue > aLoadArgs( fillLoadArgs(
                i_rConnection, _bSuppressMacros, _bReadOnly, i_rOpenCommandArguments, aEmbeddedObjectDescriptor ) );

            m_xEmbeddedObject.set(xEmbedFactory->createInstanceUserInit(aClassID
                                                                        ,sDocumentService
                                                                        ,xStorage
                                                                        ,m_pImpl->m_aProps.sPersistentName
                                                                        ,nEntryConnectionMode
                                                                        ,aLoadArgs
                                                                        ,aEmbeddedObjectDescriptor
                                                                        ),UNO_QUERY);
            if ( m_xEmbeddedObject.is() )
            {
                if ( !m_pClientHelper.is() )
                {
                    m_pClientHelper = new OEmbeddedClientHelper;
                }
                m_xEmbeddedObject->setClientSite(m_pClientHelper);
                m_xEmbeddedObject->changeState(EmbedStates::RUNNING);
                if ( bSetSize )
                {
                    LockModifiable aLockModify( impl_getComponent_throw( false ) );

                    awt::Size aSize( DEFAULT_WIDTH, DEFAULT_HEIGHT );
                    m_xEmbeddedObject->setVisualAreaSize(Aspects::MSOLE_CONTENT,aSize);
                }
            }
        }
    }
    else
    {
        sal_Int32 nCurrentState = m_xEmbeddedObject->getCurrentState();
        if ( nCurrentState == EmbedStates::LOADED )
        {
            if ( !m_pClientHelper.is() )
            {
                m_pClientHelper = new OEmbeddedClientHelper;
            }
            m_xEmbeddedObject->setClientSite(m_pClientHelper);

            Sequence< PropertyValue > aEmbeddedObjectDescriptor;
            Sequence< PropertyValue > aLoadArgs( fillLoadArgs(
                i_rConnection, _bSuppressMacros, _bReadOnly, i_rOpenCommandArguments, aEmbeddedObjectDescriptor ) );

            Reference<XCommonEmbedPersist> xCommon(m_xEmbeddedObject,UNO_QUERY);
            OSL_ENSURE(xCommon.is(),"unsupported interface!");
            if ( xCommon.is() )
                xCommon->reload( aLoadArgs, aEmbeddedObjectDescriptor );
            m_xEmbeddedObject->changeState(EmbedStates::RUNNING);
        }
        else
        {
            OSL_ENSURE( ( nCurrentState == EmbedStates::RUNNING ) || ( nCurrentState == EmbedStates::ACTIVE ),
                "ODocumentDefinition::loadEmbeddedObject: unexpected state!" );

            // if the document was already loaded (which means the embedded object is in state RUNNING or ACTIVE),
            // then just re-set some model parameters
            try
            {
                // ensure the media descriptor doesn't contain any values which are intended for the
                // EmbeddedObjectDescriptor only
                ::comphelper::NamedValueCollection aEmbeddedObjectDescriptor;
                ::comphelper::NamedValueCollection aNewMediaDesc;
                separateOpenCommandArguments( i_rOpenCommandArguments, aNewMediaDesc, aEmbeddedObjectDescriptor );

                // merge the new media descriptor into the existing media descriptor
                const Reference< XModel > xModel( getComponent(), UNO_QUERY_THROW );
                const Sequence< PropertyValue > aArgs = xModel->getArgs();
                ::comphelper::NamedValueCollection aExistentMediaDesc( aArgs );
                aExistentMediaDesc.merge( aNewMediaDesc, false );

                lcl_putLoadArgs( aExistentMediaDesc, optional_bool(), optional_bool() );
                    // don't put _bSuppressMacros and _bReadOnly here - if the document was already
                    // loaded, we should not tamper with its settings.
                    // #i88977# #i86872#

                xModel->attachResource( xModel->getURL(), aExistentMediaDesc.getPropertyValues() );
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("dbaccess");
            }
        }
    }

    // set the OfficeDatabaseDocument instance as parent of the embedded document
    // #i40358#
    Reference< XChild > xDepdendDocAsChild( getComponent(), UNO_QUERY );
    if ( xDepdendDocAsChild.is() )
    {
        try
        {
            if ( !xDepdendDocAsChild->getParent().is() )
            {   // first encounter
                xDepdendDocAsChild->setParent( getDataSource( m_xParentContainer ) );
            }
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("dbaccess");
        }
    }

    if ( i_rConnection.is() )
        m_xLastKnownConnection = i_rConnection;
}

void ODocumentDefinition::onCommandPreview(Any& _rImage)
{
    loadEmbeddedObjectForPreview();
    if ( !m_xEmbeddedObject.is() )
        return;

    try
    {
        Reference<XTransferable> xTransfer(getComponent(),UNO_QUERY);
        if ( xTransfer.is() )
        {
            DataFlavor aFlavor;
            aFlavor.MimeType = "image/png";
            aFlavor.HumanPresentableName = "Portable Network Graphics";
            aFlavor.DataType = cppu::UnoType<Sequence < sal_Int8 >>::get();

            _rImage = xTransfer->getTransferData( aFlavor );
        }
    }
    catch( const Exception& )
    {
    }
}

void ODocumentDefinition::getPropertyDefaultByHandle( sal_Int32 /*_nHandle*/, Any& _rDefault ) const
{
    _rDefault.clear();
}

void ODocumentDefinition::onCommandGetDocumentProperties( Any& _rProps )
{
    loadEmbeddedObjectForPreview();
    if ( !m_xEmbeddedObject.is() )
        return;

    try
    {
        Reference<XDocumentPropertiesSupplier> xDocSup(
            getComponent(), UNO_QUERY );
        if ( xDocSup.is() )
            _rProps <<= xDocSup->getDocumentProperties();
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}

Reference< util::XCloseable > ODocumentDefinition::impl_getComponent_throw( const bool i_ForceCreate )
{
    OSL_ENSURE(m_xEmbeddedObject.is(),"Illegal call for embeddedObject");
    Reference< util::XCloseable > xComp;
    if ( m_xEmbeddedObject.is() )
    {
        int nState = m_xEmbeddedObject->getCurrentState();
        if ( ( nState == EmbedStates::LOADED ) && i_ForceCreate )
        {
            m_xEmbeddedObject->changeState( EmbedStates::RUNNING );
            nState = m_xEmbeddedObject->getCurrentState();
            OSL_ENSURE( nState == EmbedStates::RUNNING, "ODocumentDefinition::impl_getComponent_throw: could not switch to RUNNING!" );
        }

        if ( nState == EmbedStates::ACTIVE || nState == EmbedStates::RUNNING )
        {
            if ( m_xEmbeddedObject.is() )
            {
                xComp = m_xEmbeddedObject->getComponent();
                OSL_ENSURE(xComp.is(),"No valid component");
            }
        }
    }
    return xComp;
}

Reference< util::XCloseable > ODocumentDefinition::getComponent()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    return impl_getComponent_throw();
}

namespace
{
    Reference< XDatabaseDocumentUI > lcl_getDatabaseDocumentUI( ODatabaseModelImpl const & _rModelImpl )
    {
        Reference< XDatabaseDocumentUI > xUI;

        Reference< XModel > xModel( _rModelImpl.getModel_noCreate() );
        if ( xModel.is() )
            xUI.set( xModel->getCurrentController(), UNO_QUERY );
        return xUI;
    }
}

Reference< XComponent > ODocumentDefinition::impl_openUI_nolck_throw( bool _bForEditing )
{
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
    if ( !m_pImpl || !m_pImpl->m_pDataSource )
        throw DisposedException();

    Reference< XComponent > xComponent;
    try
    {
        Reference< XDatabaseDocumentUI > xUI( lcl_getDatabaseDocumentUI( *m_pImpl->m_pDataSource ) );
        if ( !xUI.is() )
        {
            // no XDatabaseDocumentUI -> just execute the respective command
            m_bOpenInDesign = _bForEditing;
            xComponent = Reference<XComponent>(onCommandOpenSomething(Any(), true, nullptr), UNO_QUERY);
            OSL_ENSURE( xComponent.is(), "ODocumentDefinition::impl_openUI_nolck_throw: opening the thingie failed." );
            return xComponent;
        }


        OUString sName( impl_getHierarchicalName( false ) );
        sal_Int32 nObjectType = m_bForm ? DatabaseObject::FORM : DatabaseObject::REPORT;
        aGuard.clear();

        xComponent = xUI->loadComponent(
            nObjectType, sName, _bForEditing
        );
    }
    catch( const RuntimeException& ) { throw; }
    catch( const Exception& )
    {
        throw WrappedTargetException(
            OUString(), *this, ::cppu::getCaughtException() );
    }

    return xComponent;
}

void ODocumentDefinition::impl_store_throw()
{
    Reference<XEmbedPersist> xPersist( m_xEmbeddedObject, UNO_QUERY );
    if ( xPersist.is() )
    {
        xPersist->storeOwn();
        notifyDataSourceModified();
    }
}

bool ODocumentDefinition::impl_close_throw()
{
    bool bSuccess = prepareClose();
    if ( bSuccess && m_xEmbeddedObject.is() )
    {
        m_xEmbeddedObject->changeState( EmbedStates::LOADED );
        bSuccess = m_xEmbeddedObject->getCurrentState() == EmbedStates::LOADED;
    }
    return bSuccess;
}

Reference< XComponent > SAL_CALL ODocumentDefinition::open(  )
{
    return impl_openUI_nolck_throw( false );
}

Reference< XComponent > SAL_CALL ODocumentDefinition::openDesign(  )
{
    return impl_openUI_nolck_throw( true );
}

void SAL_CALL ODocumentDefinition::store(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );
    try
    {
        impl_store_throw();
    }
    catch( const RuntimeException& ) { throw; }
    catch( const Exception& )
    {
        throw WrappedTargetException(
            OUString(), *this, ::cppu::getCaughtException() );
    }
}

sal_Bool SAL_CALL ODocumentDefinition::close(  )
{
    ::osl::MutexGuard aGuard( m_aMutex );

    bool bSuccess = false;
    try
    {
        bSuccess = impl_close_throw();
    }
    catch( const RuntimeException& ) { throw; }
    catch( const Exception& )
    {
        throw WrappedTargetException(
            OUString(), *this, ::cppu::getCaughtException() );
    }
    return bSuccess;
}

OUString SAL_CALL ODocumentDefinition::getHierarchicalName()
{
    ::osl::MutexGuard aGuard( m_aMutex );
    return impl_getHierarchicalName( false );
}

OUString SAL_CALL ODocumentDefinition::composeHierarchicalName( const OUString& i_rRelativeName )
{
    return getHierarchicalName() + "/" + i_rRelativeName;
}

void SAL_CALL ODocumentDefinition::rename( const OUString& _rNewName )
{
    try
    {
        ::osl::ResettableMutexGuard aGuard(m_aMutex);
        if ( _rNewName == m_pImpl->m_aProps.aTitle )
            return;

        // document definitions are organized in a hierarchical way, so reject names
        // which contain a /, as this is reserved for hierarchy level separation
        if ( _rNewName.indexOf( '/' ) != -1 )
            m_aErrorHelper.raiseException( ErrorCondition::DB_OBJECT_NAME_WITH_SLASHES, *this );

        NameChangeNotifier aNameChangeAndNotify( *this, _rNewName, aGuard );
        m_pImpl->m_aProps.aTitle = _rNewName;

        if ( m_xEmbeddedObject.is() && m_xEmbeddedObject->getCurrentState() == EmbedStates::ACTIVE )
            updateDocumentTitle();
    }
    catch(const PropertyVetoException&)
    {
        throw ElementExistException(_rNewName,*this);
    }
}

Reference< XStorage> ODocumentDefinition::getContainerStorage() const
{
    return  m_pImpl->m_pDataSource
        ?   m_pImpl->m_pDataSource->getStorage( m_bForm ? ODatabaseModelImpl::ObjectType::Form : ODatabaseModelImpl::ObjectType::Report )
        :   Reference< XStorage>();
}

bool ODocumentDefinition::isModified()
{
    osl::ClearableGuard< osl::Mutex > aGuard(m_aMutex);
    bool bRet = false;
    if ( m_xEmbeddedObject.is() )
    {
        Reference<XModifiable> xModel(getComponent(),UNO_QUERY);
        bRet = xModel.is() && xModel->isModified();
    }
    return bRet;
}

bool ODocumentDefinition::prepareClose()
{
    if ( !m_xEmbeddedObject.is() )
        return true;

    try
    {
        // suspend the controller. Embedded objects are not allowed to raise
        // own UI at their own discretion, instead, this has always to be triggered
        // by the embedding component. Thus, we do the suspend call here.
        // #i49370#

        Reference< util::XCloseable > xComponent( impl_getComponent_throw( false ) );
        if ( !xComponent.is() )
            return true;

        Reference< XModel > xModel( xComponent, UNO_QUERY );
        Reference< XController > xController;
        if ( xModel.is() )
            xController = xModel->getCurrentController();

        OSL_ENSURE( xController.is() || ( m_xEmbeddedObject->getCurrentState() < EmbedStates::ACTIVE ),
            "ODocumentDefinition::prepareClose: no controller!" );
        if ( !xController.is() )
            // document has not yet been activated, i.e. has no UI, yet
            return true;

        if (!xController->suspend(true))
            // controller vetoed the closing
            return false;

        if ( isModified() )
        {
            Reference< XFrame > xFrame( xController->getFrame() );
            Reference<XTopWindow> xTopWindow;
            if ( xFrame.is() )
            {
                xTopWindow = Reference<XTopWindow>(xFrame->getContainerWindow(), UNO_QUERY_THROW);
                xTopWindow->toFront();
            }
            if (!save(true, xTopWindow))
            {
                // revert suspension
                xController->suspend(false);
                // saving failed or was cancelled
                return false;
            }
        }
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }

    return true;
}

void ODocumentDefinition::fillReportData( const Reference< XComponentContext >& _rContext,
                                               const Reference< util::XCloseable >& _rxComponent,
                                               const Reference< XConnection >& _rxActiveConnection )
{
    uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
    {
        {"TextDocument", uno::Any(_rxComponent)},
        {"ActiveConnection", uno::Any(_rxActiveConnection)}
    }));
    try
    {
        Reference< XJobExecutor > xExecutable(
            _rContext->getServiceManager()->createInstanceWithArgumentsAndContext(u"com.sun.star.wizards.report.CallReportWizard"_ustr, aArgs, _rContext), UNO_QUERY_THROW );
        xExecutable->trigger( u"fill"_ustr );
    }
    catch( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("dbaccess");
    }
}

void ODocumentDefinition::updateDocumentTitle()
{
    OUString sName = m_pImpl->m_aProps.aTitle;
    if ( m_pImpl->m_pDataSource )
    {
        if ( sName.isEmpty() )
        {
            if ( m_bForm )
                sName = DBA_RES( RID_STR_FORM );
            else
                sName = DBA_RES( RID_STR_REPORT );
            rtl::Reference< ODatabaseDocument > xUntitledProvider(m_pImpl->m_pDataSource->getModel_noCreate() );
            if ( xUntitledProvider.is() )
                sName += OUString::number( xUntitledProvider->leaseNumber(getComponent()) );
        }

        rtl::Reference< ODatabaseDocument > xDatabaseDocumentModel(m_pImpl->m_pDataSource->getModel_noCreate());
        if ( xDatabaseDocumentModel.is() )
            sName = xDatabaseDocumentModel->getTitle() + " : " + sName;
    }
    Reference< XTitle> xTitle(getComponent(),UNO_QUERY);
    if ( xTitle.is() )
        xTitle->setTitle(sName);
}

void SAL_CALL ODocumentDefinition::queryClosing( const lang::EventObject&, sal_Bool )
{
    try
    {
        if ( !close() )
            throw util::CloseVetoException();
    }
    catch(const lang::WrappedTargetException&)
    {
        throw util::CloseVetoException();
    }
}

void SAL_CALL ODocumentDefinition::notifyClosing( const lang::EventObject& /*Source*/ )
{
}

void SAL_CALL ODocumentDefinition::disposing( const lang::EventObject& /*Source*/ )
{
}

void ODocumentDefinition::firePropertyChange( sal_Int32 i_nHandle, const Any& i_rNewValue, const Any& i_rOldValue,
        bool i_bVetoable, const NotifierAccess& )
{
    fire( &i_nHandle, &i_rNewValue, &i_rOldValue, 1, i_bVetoable );
}

// NameChangeNotifier
NameChangeNotifier::NameChangeNotifier( ODocumentDefinition& i_rDocumentDefinition, const OUString& i_rNewName,
                                  ::osl::ResettableMutexGuard& i_rClearForNotify )
    :m_rDocumentDefinition( i_rDocumentDefinition )
    ,m_aOldValue( Any( i_rDocumentDefinition.getCurrentName() ) )
    ,m_aNewValue( Any( i_rNewName ) )
    ,m_rClearForNotify( i_rClearForNotify )
{
    impl_fireEvent_throw( true );
}

NameChangeNotifier::~NameChangeNotifier()
{
    impl_fireEvent_throw( false );
}

void NameChangeNotifier::impl_fireEvent_throw( const bool i_bVetoable )
{
    m_rClearForNotify.clear();
    m_rDocumentDefinition.firePropertyChange(
        PROPERTY_ID_NAME, m_aNewValue, m_aOldValue, i_bVetoable, ODocumentDefinition::NotifierAccess() );
    m_rClearForNotify.reset();
}

}   // namespace dbaccess

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
