/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <memory>
#include <fmdocumentclassification.hxx>
#include <fmobj.hxx>
#include <fmpgeimp.hxx>
#include <fmprop.hxx>
#include <svx/strings.hrc>
#include <fmservs.hxx>
#include <fmshimp.hxx>
#include <svx/fmtools.hxx>
#include <fmvwimp.hxx>
#include <formcontrolfactory.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <svx/svditer.hxx>
#include <svx/dataaccessdescriptor.hxx>
#include <svx/dialmgr.hxx>
#include <svx/svdobjkind.hxx>
#include <svx/fmmodel.hxx>
#include <svx/fmpage.hxx>
#include <svx/fmshell.hxx>
#include <svx/fmview.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdpagv.hxx>
#include <svx/xmlexchg.hxx>
#include <toolkit/helper/vclunohelper.hxx>

#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/util/XNumberFormats.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/form/FormButtonType.hpp>
#include <com/sun/star/form/binding/XBindableValue.hpp>
#include <com/sun/star/form/binding/XValueBinding.hpp>
#include <com/sun/star/form/runtime/FormController.hpp>
#include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
#include <com/sun/star/awt/XTabControllerModel.hpp>
#include <com/sun/star/awt/XControlContainer.hpp>
#include <com/sun/star/awt/XTabController.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/awt/XControl.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/container/XContainer.hpp>

#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/property.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <unotools/moduleoptions.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <sal/log.hxx>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/window.hxx>
#include <connectivity/dbtools.hxx>

#include <algorithm>

using namespace ::comphelper;
using namespace ::svx;
using namespace ::svxform;
using namespace ::dbtools;

    using namespace ::com::sun::star;
    using ::com::sun::star::uno::Exception;
    using ::com::sun::star::uno::XInterface;
    using ::com::sun::star::uno::Sequence;
    using ::com::sun::star::uno::UNO_QUERY;
    using ::com::sun::star::uno::UNO_QUERY_THROW;
    using ::com::sun::star::uno::UNO_SET_THROW;
    using ::com::sun::star::uno::Type;
    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::uno::Any;
    using ::com::sun::star::uno::XComponentContext;
    using ::com::sun::star::form::FormButtonType_SUBMIT;
    using ::com::sun::star::form::binding::XValueBinding;
    using ::com::sun::star::form::binding::XBindableValue;
    using ::com::sun::star::lang::XComponent;
    using ::com::sun::star::container::XIndexAccess;
    using ::com::sun::star::form::runtime::FormController;
    using ::com::sun::star::form::runtime::XFormController;
    using ::com::sun::star::script::XEventAttacherManager;
    using ::com::sun::star::awt::XTabControllerModel;
    using ::com::sun::star::container::XChild;
    using ::com::sun::star::task::XInteractionHandler;
    using ::com::sun::star::awt::XTabController;
    using ::com::sun::star::awt::XControlContainer;
    using ::com::sun::star::awt::XControl;
    using ::com::sun::star::form::XFormComponent;
    using ::com::sun::star::form::XForm;
    using ::com::sun::star::lang::IndexOutOfBoundsException;
    using ::com::sun::star::container::XContainer;
    using ::com::sun::star::container::ContainerEvent;
    using ::com::sun::star::lang::EventObject;
    using ::com::sun::star::sdb::SQLErrorEvent;
    using ::com::sun::star::sdbc::XRowSet;
    using ::com::sun::star::beans::XPropertySet;
    using ::com::sun::star::container::XElementAccess;
    using ::com::sun::star::awt::XWindow;
    using ::com::sun::star::awt::FocusEvent;
    using ::com::sun::star::ui::dialogs::XExecutableDialog;
    using ::com::sun::star::sdbc::XDataSource;
    using ::com::sun::star::container::XIndexContainer;
    using ::com::sun::star::sdbc::XConnection;
    using ::com::sun::star::container::XNameAccess;
    using ::com::sun::star::sdbc::SQLException;
    using ::com::sun::star::util::XNumberFormatsSupplier;
    using ::com::sun::star::util::XNumberFormats;
    using ::com::sun::star::beans::XPropertySetInfo;

    namespace FormComponentType = ::com::sun::star::form::FormComponentType;
    namespace CommandType = ::com::sun::star::sdb::CommandType;
    namespace DataType = ::com::sun::star::sdbc::DataType;


class FmXFormView::ObjectRemoveListener : public SfxListener
{
    FmXFormView* m_pParent;
public:
    explicit ObjectRemoveListener( FmXFormView* pParent );
    virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
};

FormViewPageWindowAdapter::FormViewPageWindowAdapter( css::uno::Reference<css::uno::XComponentContext> _xContext, const SdrPageWindow& _rWindow, FmXFormView* _pViewImpl )
:   m_xControlContainer( _rWindow.GetControlContainer() ),
    m_xContext(std::move( _xContext )),
    m_pViewImpl( _pViewImpl ),
    m_pWindow( _rWindow.GetPaintWindow().GetOutputDevice().GetOwnerWindow() )
{

    // create an XFormController for every form
    FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( _rWindow.GetPageView().GetPage() );
    DBG_ASSERT( pFormPage, "FormViewPageWindowAdapter::FormViewPageWindowAdapter: no FmFormPage found!" );
    if ( !pFormPage )
        return;

    try
    {
        Reference< XIndexAccess > xForms( pFormPage->GetForms(), UNO_QUERY_THROW );
        sal_uInt32 nLength = xForms->getCount();
        for (sal_uInt32 i = 0; i < nLength; i++)
        {
            Reference< XForm > xForm( xForms->getByIndex(i), UNO_QUERY );
            if ( xForm.is() )
                setController( xForm, nullptr );
        }
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}

FormViewPageWindowAdapter::~FormViewPageWindowAdapter()
{
}

void FormViewPageWindowAdapter::dispose()
{
    for (   ::std::vector< Reference< XFormController > >::const_iterator i = m_aControllerList.begin();
            i != m_aControllerList.end();
            ++i
        )
    {
        try
        {
            Reference< XFormController > xController( *i, UNO_SET_THROW );

            // detaching the events
            Reference< XChild > xControllerModel( xController->getModel(), UNO_QUERY );
            if ( xControllerModel.is() )
            {
                Reference< XEventAttacherManager >  xEventManager( xControllerModel->getParent(), UNO_QUERY_THROW );
                Reference< XInterface > xControllerNormalized( xController, UNO_QUERY_THROW );
                xEventManager->detach( i - m_aControllerList.begin(), xControllerNormalized );
            }

            // dispose the formcontroller
            xController->dispose();
        }
        catch (const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
    }

    m_aControllerList.clear();
}

sal_Bool SAL_CALL FormViewPageWindowAdapter::hasElements()
{
    return getCount() != 0;
}

Type SAL_CALL  FormViewPageWindowAdapter::getElementType()
{
    return cppu::UnoType<XFormController>::get();
}

// XIndexAccess
sal_Int32 SAL_CALL FormViewPageWindowAdapter::getCount()
{
    return m_aControllerList.size();
}

Any SAL_CALL FormViewPageWindowAdapter::getByIndex(sal_Int32 nIndex)
{
    if (nIndex < 0 ||
        nIndex >= getCount())
        throw IndexOutOfBoundsException();

    Any aElement;
    aElement <<= m_aControllerList[nIndex];
    return aElement;
}

void SAL_CALL FormViewPageWindowAdapter::makeVisible( const Reference< XControl >& Control )
{
    SolarMutexGuard aSolarGuard;

    Reference< XWindow >  xWindow( Control, UNO_QUERY );
    if ( xWindow.is() && m_pViewImpl->getView() && m_pWindow )
    {
        awt::Rectangle aRect = xWindow->getPosSize();
        ::tools::Rectangle aNewRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
        aNewRect = m_pWindow->PixelToLogic( aNewRect );
        m_pViewImpl->getView()->MakeVisible( aNewRect, *m_pWindow );
    }
}

static Reference< XFormController >  getControllerSearchChildren( const Reference< XIndexAccess > & xIndex, const Reference< XTabControllerModel > & xModel)
{
    if (xIndex.is() && xIndex->getCount())
    {
        Reference< XFormController >  xController;

        for (sal_Int32 n = xIndex->getCount(); n-- && !xController.is(); )
        {
            xIndex->getByIndex(n) >>= xController;
            if (xModel.get() == xController->getModel().get())
                return xController;
            else
            {
                xController = getControllerSearchChildren(xController, xModel);
                if ( xController.is() )
                    return xController;
            }
        }
    }
    return Reference< XFormController > ();
}

// Search the according controller
Reference< XFormController >  FormViewPageWindowAdapter::getController( const Reference< XForm > & xForm ) const
{
    Reference< XTabControllerModel >  xModel(xForm, UNO_QUERY);
    for (const auto& rpController : m_aControllerList)
    {
        if (rpController->getModel().get() == xModel.get())
            return rpController;

        // the current-round controller isn't the right one. perhaps one of its children ?
        Reference< XFormController >  xChildSearch = getControllerSearchChildren(Reference< XIndexAccess > (rpController, UNO_QUERY), xModel);
        if (xChildSearch.is())
            return xChildSearch;
    }
    return Reference< XFormController > ();
}


void FormViewPageWindowAdapter::setController(const Reference< XForm > & xForm, const Reference< XFormController >& _rxParentController )
{
    DBG_ASSERT( xForm.is(), "FormViewPageWindowAdapter::setController: there should be a form!" );
    Reference< XIndexAccess >  xFormCps(xForm, UNO_QUERY);
    if (!xFormCps.is())
        return;

    Reference< XTabControllerModel >  xTabOrder(xForm, UNO_QUERY);

    // create a form controller
    Reference< XFormController > xController( FormController::create(m_xContext) );

    Reference< XInteractionHandler > xHandler;
    if ( _rxParentController.is() )
        xHandler = _rxParentController->getInteractionHandler();
    else
    {
        // TODO: should we create a default handler? Not really necessary, since the
        // FormController itself has a default fallback
    }
    if ( xHandler.is() )
        xController->setInteractionHandler( xHandler );

    xController->setContext( this );

    xController->setModel( xTabOrder );
    xController->setContainer( m_xControlContainer );
    xController->activateTabOrder();
    xController->addActivateListener( m_pViewImpl );

    if ( _rxParentController.is() )
        _rxParentController->addChildController( xController );
    else
    {
        m_aControllerList.push_back(xController);

        xController->setParent( *this );

        // attaching the events
        Reference< XEventAttacherManager > xEventManager( xForm->getParent(), UNO_QUERY );
        xEventManager->attach(m_aControllerList.size() - 1, Reference<XInterface>( xController, UNO_QUERY ), Any(xController) );
    }

    // now go through the subforms
    sal_uInt32 nLength = xFormCps->getCount();
    Reference< XForm >  xSubForm;
    for (sal_uInt32 i = 0; i < nLength; i++)
    {
        if ( xFormCps->getByIndex(i) >>= xSubForm )
            setController( xSubForm, xController );
    }
}


void FormViewPageWindowAdapter::updateTabOrder( const Reference< XForm >& _rxForm )
{
    OSL_PRECOND( _rxForm.is(), "FormViewPageWindowAdapter::updateTabOrder: illegal argument!" );
    if ( !_rxForm.is() )
        return;

    try
    {
        Reference< XTabController > xTabCtrl( getController( _rxForm ) );
        if ( xTabCtrl.is() )
        {   // if there already is a TabController for this form, then delegate the "updateTabOrder" request
            xTabCtrl->activateTabOrder();
        }
        else
        {   // otherwise, create a TabController

            // if it's a sub form, then we must ensure there exist TabControllers
            // for all its ancestors, too
            Reference< XForm > xParentForm( _rxForm->getParent(), UNO_QUERY );
            // there is a parent form -> look for the respective controller
            Reference< XFormController > xParentController;
            if ( xParentForm.is() )
                xParentController = getController( xParentForm );

            setController( _rxForm, xParentController );
        }
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


FmXFormView::FmXFormView(FmFormView* _pView )
    :m_pMarkedGrid(nullptr)
    ,m_pView(_pView)
    ,m_nActivationEvent(nullptr)
    ,m_nErrorMessageEvent( nullptr )
    ,m_nAutoFocusEvent( nullptr )
    ,m_nControlWizardEvent( nullptr )
    ,m_bFirstActivation( true )
    ,m_isTabOrderUpdateSuspended( false )
{
}


void FmXFormView::cancelEvents()
{
    if ( m_nActivationEvent )
    {
        Application::RemoveUserEvent( m_nActivationEvent );
        m_nActivationEvent = nullptr;
    }

    if ( m_nErrorMessageEvent )
    {
        Application::RemoveUserEvent( m_nErrorMessageEvent );
        m_nErrorMessageEvent = nullptr;
    }

    if ( m_nAutoFocusEvent )
    {
        Application::RemoveUserEvent( m_nAutoFocusEvent );
        m_nAutoFocusEvent = nullptr;
    }

    if ( m_nControlWizardEvent )
    {
        Application::RemoveUserEvent( m_nControlWizardEvent );
        m_nControlWizardEvent = nullptr;
    }
}


void FmXFormView::notifyViewDying( )
{
    DBG_ASSERT( m_pView, "FmXFormView::notifyViewDying: my view already died!" );
    m_pView = nullptr;
    cancelEvents();
}


FmXFormView::~FmXFormView()
{
    DBG_ASSERT( m_aPageWindowAdapters.empty(), "FmXFormView::~FmXFormView: Window list not empty!" );
    for (const auto& rpAdapter : m_aPageWindowAdapters)
    {
        rpAdapter->dispose();
    }

    cancelEvents();
}

//      EventListener

void SAL_CALL FmXFormView::disposing(const EventObject& Source)
{
    if ( m_xWindow.is() && Source.Source == m_xWindow )
    {
        m_xWindow->removeFocusListener(this);
        if ( m_pView )
        {
            m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
        }
        m_xWindow = nullptr;
    }
}

// XFormControllerListener

void SAL_CALL FmXFormView::formActivated(const EventObject& rEvent)
{
    if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
        m_pView->GetFormShell()->GetImpl()->formActivated( rEvent );
}


void SAL_CALL FmXFormView::formDeactivated(const EventObject& rEvent)
{
    if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
        m_pView->GetFormShell()->GetImpl()->formDeactivated( rEvent );
}

// XContainerListener

void SAL_CALL FmXFormView::elementInserted(const ContainerEvent& evt)
{
    try
    {
        Reference< XControlContainer > xControlContainer( evt.Source, UNO_QUERY_THROW );
        Reference< XControl > xControl( evt.Element, UNO_QUERY_THROW );
        Reference< XFormComponent > xControlModel( xControl->getModel(), UNO_QUERY_THROW );
        Reference< XForm > xForm( xControlModel->getParent(), UNO_QUERY_THROW );

        if ( m_isTabOrderUpdateSuspended )
        {
            // remember the container and the control, so we can update the tab order on resumeTabOrderUpdate
            m_aNeedTabOrderUpdate[ xControlContainer ].insert( xForm );
        }
        else
        {
            rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( xControlContainer );
            if ( pAdapter.is() )
                pAdapter->updateTabOrder( xForm );
        }
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


void SAL_CALL FmXFormView::elementReplaced(const ContainerEvent& evt)
{
    elementInserted(evt);
}


void SAL_CALL FmXFormView::elementRemoved(const ContainerEvent& /*evt*/)
{
}


rtl::Reference< FormViewPageWindowAdapter > FmXFormView::findWindow( const Reference< XControlContainer >& _rxCC )  const
{
    auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
        [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
    if (i != m_aPageWindowAdapters.end())
        return *i;
    return nullptr;
}


void FmXFormView::addWindow(const SdrPageWindow& rWindow)
{
    FmFormPage* pFormPage = dynamic_cast<FmFormPage*>( rWindow.GetPageView().GetPage()  );
    if ( !pFormPage )
        return;

    const rtl::Reference< UnoControlContainer > & xCC = rWindow.GetControlContainer();
    if  (   xCC.is()
        &&  ( !findWindow( xCC ).is() )
        )
    {
        rtl::Reference< FormViewPageWindowAdapter > pAdapter = new FormViewPageWindowAdapter( comphelper::getProcessComponentContext(), rWindow, this );
        m_aPageWindowAdapters.push_back( pAdapter );

        // listen at the ControlContainer to notice changes
        xCC->addContainerListener( this );
    }
}


void FmXFormView::removeWindow( const Reference< XControlContainer >& _rxCC )
{
    // Is called if
    // - the design mode is being switched to
    // - a window is deleted while in the design mode
    // - the control container for a window is removed while the active mode is on

    auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
        [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
    if (i != m_aPageWindowAdapters.end())
    {
        Reference< XContainer >  xContainer( _rxCC, UNO_QUERY );
        if ( xContainer.is() )
            xContainer->removeContainerListener( this );

        (*i)->dispose();
        m_aPageWindowAdapters.erase( i );
    }
}

void FmXFormView::displayAsyncErrorMessage( const SQLErrorEvent& _rEvent )
{
    DBG_ASSERT( nullptr == m_nErrorMessageEvent, "FmXFormView::displayAsyncErrorMessage: not too fast, please!" );
        // This should not happen - usually, the PostUserEvent is faster than any possible user
        // interaction which could trigger a new error. If it happens, we need a queue for the events.
    m_aAsyncError = _rEvent;
    m_nErrorMessageEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnDelayedErrorMessage ) );
}

IMPL_LINK_NOARG(FmXFormView, OnDelayedErrorMessage, void*, void)
{
    m_nErrorMessageEvent = nullptr;
    displayException(m_aAsyncError, GetParentWindow());
}

void FmXFormView::onFirstViewActivation( const FmFormModel* _pDocModel )
{
    if ( _pDocModel && _pDocModel->GetAutoControlFocus() )
        m_nAutoFocusEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnAutoFocus ) );
}

void FmXFormView::suspendTabOrderUpdate()
{
    OSL_ENSURE( !m_isTabOrderUpdateSuspended, "FmXFormView::suspendTabOrderUpdate: nesting not allowed!" );
    m_isTabOrderUpdateSuspended = true;
}

void FmXFormView::resumeTabOrderUpdate()
{
    OSL_ENSURE( m_isTabOrderUpdateSuspended, "FmXFormView::resumeTabOrderUpdate: not suspended!" );
    m_isTabOrderUpdateSuspended = false;

    // update the tab orders for all components which were collected since the suspendTabOrderUpdate call.
    for (const auto& rContainer : m_aNeedTabOrderUpdate)
    {
        rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( rContainer.first );
        if ( !pAdapter.is() )
            continue;

        for (const auto& rForm : rContainer.second)
        {
            pAdapter->updateTabOrder( rForm );
        }
    }
    m_aNeedTabOrderUpdate.clear();
}

namespace
{
    bool isActivableDatabaseForm(const Reference< XFormController > &xController)
    {
        // only database forms are to be activated
        Reference< XRowSet >  xForm(xController->getModel(), UNO_QUERY);
        if ( !xForm.is() || !getConnection( xForm ).is() )
            return false;

        Reference< XPropertySet > xFormSet( xForm, UNO_QUERY );
        if ( !xFormSet.is() )
        {
            SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form which does not have properties?" );
            return false;
        }

        const OUString aSource = ::comphelper::getString( xFormSet->getPropertyValue( FM_PROP_COMMAND ) );

        return !aSource.isEmpty();
    }

    class find_active_databaseform
    {
        const Reference< XFormController > xActiveController;

    public:

        explicit find_active_databaseform( const Reference< XFormController >& _xActiveController )
            : xActiveController(_xActiveController )
        {}

        Reference < XFormController > operator() (const Reference< XFormController > &xController)
        {
            if(xController == xActiveController && isActivableDatabaseForm(xController))
                return xController;

            if ( !xController.is() )
            {
                SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form controller which does not have children?" );
                return nullptr;
            }

            for(sal_Int32 i = 0; i < xController->getCount(); ++i)
            {
                const Any a(xController->getByIndex(i));
                Reference < XFormController > xI;
                if ((a >>= xI) && xI.is())
                {
                    Reference < XFormController > xRes(operator()(xI));
                    if (xRes.is())
                        return xRes;
                }
            }

            return nullptr;
        }
    };
}


IMPL_LINK_NOARG(FmXFormView, OnActivate, void*, void)
{
    m_nActivationEvent = nullptr;

    if ( !m_pView )
    {
        OSL_FAIL( "FmXFormView::OnActivate: well... seems we have a timing problem (the view already died)!" );
        return;
    }

    // setting the controller to activate
    if (!(m_pView->GetFormShell() && m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW))
        return;

    FmXFormShell* const pShImpl =  m_pView->GetFormShell()->GetImpl();

    if(!pShImpl)
        return;

    find_active_databaseform fad(pShImpl->getActiveController_Lock());

    vcl::Window* pWindow = m_pView->GetActualOutDev()->GetOwnerWindow();
    rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
    for (const auto& rpPageWindowAdapter : m_aPageWindowAdapters)
    {
        if ( pWindow == rpPageWindowAdapter->getWindow() )
            pAdapter = rpPageWindowAdapter;
    }

    if ( !pAdapter.is() )
        return;

    Reference< XFormController > xControllerToActivate;
    for (const Reference< XFormController > & xController : pAdapter->GetList())
    {
        if ( !xController.is() )
            continue;

        {
            Reference< XFormController > xActiveController(fad(xController));
            if (xActiveController.is())
            {
                xControllerToActivate = std::move(xActiveController);
                break;
            }
        }

        if(xControllerToActivate.is() || !isActivableDatabaseForm(xController))
            continue;

        xControllerToActivate = xController;
    }
    pShImpl->setActiveController_Lock(xControllerToActivate);
}


void FmXFormView::Activate(bool bSync)
{
    if (m_nActivationEvent)
    {
        Application::RemoveUserEvent(m_nActivationEvent);
        m_nActivationEvent = nullptr;
    }

    if (bSync)
    {
        LINK(this,FmXFormView,OnActivate).Call(nullptr);
    }
    else
        m_nActivationEvent = Application::PostUserEvent(LINK(this,FmXFormView,OnActivate));
}


void FmXFormView::Deactivate(bool bDeactivateController)
{
    if (m_nActivationEvent)
    {
        Application::RemoveUserEvent(m_nActivationEvent);
        m_nActivationEvent = nullptr;
    }

    FmXFormShell* pShImpl =  m_pView->GetFormShell() ? m_pView->GetFormShell()->GetImpl() : nullptr;
    if (pShImpl && bDeactivateController)
        pShImpl->setActiveController_Lock(nullptr);
}


FmFormShell* FmXFormView::GetFormShell() const
{
    return m_pView ? m_pView->GetFormShell() : nullptr;
}

void FmXFormView::AutoFocus()
{
    if (m_nAutoFocusEvent)
        Application::RemoveUserEvent(m_nAutoFocusEvent);

    m_nAutoFocusEvent = Application::PostUserEvent(LINK(this, FmXFormView, OnAutoFocus));
}


bool FmXFormView::isFocusable( const Reference< XControl >& i_rControl )
{
    if ( !i_rControl.is() )
        return false;

    try
    {
        Reference< XPropertySet > xModelProps( i_rControl->getModel(), UNO_QUERY_THROW );

        // only enabled controls are allowed to participate
        bool bEnabled = false;
        OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_ENABLED ) >>= bEnabled );
        if ( !bEnabled )
            return false;

        // check the class id of the control model
        sal_Int16 nClassId = FormComponentType::CONTROL;
        OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );

        // controls which are not focussable
        if  (   ( FormComponentType::CONTROL != nClassId )
            &&  ( FormComponentType::IMAGEBUTTON != nClassId )
            &&  ( FormComponentType::GROUPBOX != nClassId )
            &&  ( FormComponentType::FIXEDTEXT != nClassId )
            &&  ( FormComponentType::HIDDENCONTROL != nClassId )
            &&  ( FormComponentType::IMAGECONTROL != nClassId )
            &&  ( FormComponentType::SCROLLBAR != nClassId )
            &&  ( FormComponentType::SPINBUTTON!= nClassId )
            )
        {
            return true;
        }
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
    return false;
}


static Reference< XControl > lcl_firstFocussableControl( const Sequence< Reference< XControl > >& _rControls )
{
    Reference< XControl > xReturn;

    // loop through all the controls
    for ( auto const & control : _rControls )
    {
        if ( !control.is() )
            continue;

        if ( FmXFormView::isFocusable( control ) )
        {
            xReturn = control;
            break;
        }
    }

    if ( !xReturn.is() && _rControls.hasElements() )
        xReturn = _rControls[0];

    return xReturn;
}


namespace
{

    void lcl_ensureControlsOfFormExist_nothrow( const SdrPage& _rPage, const SdrView& _rView, const vcl::Window& _rWindow, const Reference< XForm >& _rxForm )
    {
        try
        {
            Reference< XInterface > xNormalizedForm( _rxForm, UNO_QUERY_THROW );

            SdrObjListIter aSdrObjectLoop( &_rPage, SdrIterMode::DeepNoGroups );
            while ( aSdrObjectLoop.IsMore() )
            {
                FmFormObj* pFormObject = FmFormObj::GetFormObject( aSdrObjectLoop.Next() );
                if ( !pFormObject )
                    continue;

                Reference< XChild > xModel( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
                Reference< XInterface > xModelParent( xModel->getParent(), UNO_QUERY );

                if ( xNormalizedForm.get() != xModelParent.get() )
                    continue;

                pFormObject->GetUnoControl( _rView, *_rWindow.GetOutDev() );
            }
        }
        catch (const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
    }
}


Reference< XFormController > FmXFormView::getFormController( const Reference< XForm >& _rxForm, const OutputDevice& _rDevice ) const
{
    Reference< XFormController > xController;

    for (const rtl::Reference< FormViewPageWindowAdapter >& pAdapter : m_aPageWindowAdapters)
    {
        if ( !pAdapter )
        {
            SAL_WARN( "svx.form", "FmXFormView::getFormController: invalid page window adapter!" );
            continue;
        }

        if ( pAdapter->getWindow() != _rDevice.GetOwnerWindow() )
            // wrong device
            continue;

        xController = pAdapter->getController( _rxForm );
        if ( xController.is() )
            break;
    }
    return xController;
}


IMPL_LINK_NOARG(FmXFormView, OnAutoFocus, void*, void)
{
    m_nAutoFocusEvent = nullptr;

    // go to the first form of our page, examine it's TabController, go to its first (in terms of the tab order)
    // control, give it the focus

    SdrPageView *pPageView = m_pView ? m_pView->GetSdrPageView() : nullptr;
    SdrPage *pSdrPage = pPageView ? pPageView->GetPage() : nullptr;
    // get the forms collection of the page we belong to
    FmFormPage* pPage = dynamic_cast<FmFormPage*>( pSdrPage  );
    Reference< XIndexAccess > xForms( pPage ? Reference< XIndexAccess >( pPage->GetForms() ) : Reference< XIndexAccess >() );

    const rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
    const vcl::Window* pWindow = pAdapter ? pAdapter->getWindow() : nullptr;

    ENSURE_OR_RETURN_VOID( xForms.is() && pWindow, "FmXFormView::OnAutoFocus: could not collect all essentials!" );

    try
    {
        // go for the tab controller of the first form
        if ( !xForms->getCount() )
            return;
        Reference< XForm > xForm( xForms->getByIndex( 0 ), UNO_QUERY_THROW );
        Reference< XTabController > xTabController( pAdapter->getController( xForm ), UNO_QUERY_THROW );

        // go for the first control of the controller
        Sequence< Reference< XControl > > aControls( xTabController->getControls() );
        if ( !aControls.hasElements() )
        {
            Reference< XElementAccess > xFormElementAccess( xForm, UNO_QUERY_THROW );
            if (xFormElementAccess->hasElements() && pPage && m_pView)
            {
                // there are control models in the form, but no controls, yet.
                // Well, since some time controls are created on demand only. In particular,
                // they're normally created when they're first painted.
                // Unfortunately, the FormController does not have any way to
                // trigger the creation itself, so we must hack this ...
                lcl_ensureControlsOfFormExist_nothrow( *pPage, *m_pView, *pWindow, xForm );
                aControls = xTabController->getControls();
                OSL_ENSURE( aControls.hasElements(), "FmXFormView::OnAutoFocus: no controls at all!" );
            }
        }

        // set the focus to this first control
        Reference< XWindow > xControlWindow( lcl_firstFocussableControl( aControls ), UNO_QUERY );
        if ( !xControlWindow.is() )
            return;

        xControlWindow->setFocus();

        // ensure that the control is visible
        // 80210 - 12/07/00 - FS
        const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
        const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
        if ( pCurrentWindow )
        {
            awt::Rectangle aRect = xControlWindow->getPosSize();
            ::tools::Rectangle aNonUnoRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
            m_pView->MakeVisible( pCurrentWindow->PixelToLogic( aNonUnoRect ), *const_cast< vcl::Window* >( pCurrentWindow ) );
        }
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
}


void FmXFormView::onCreatedFormObject( FmFormObj const & _rFormObject )
{
    FmFormShell* pShell = m_pView ? m_pView->GetFormShell() : nullptr;
    FmXFormShell* pShellImpl = pShell ? pShell->GetImpl() : nullptr;
    OSL_ENSURE( pShellImpl, "FmXFormView::onCreatedFormObject: no form shell!" );
    if ( !pShellImpl )
        return;

    // it is valid that the form shell's forms collection is not initialized, yet
    pShellImpl->UpdateForms_Lock(true);

    m_xLastCreatedControlModel.set( _rFormObject.GetUnoControlModel(), UNO_QUERY );
    if ( !m_xLastCreatedControlModel.is() )
        return;

    // some initial property defaults
    FormControlFactory aControlFactory;
    aControlFactory.initializeControlModel(pShellImpl->getDocumentType_Lock(), _rFormObject);

    if (!pShellImpl->GetWizardUsing_Lock())
        return;

    // #i31958# don't call wizards in XForms mode
    if (pShellImpl->isEnhancedForm_Lock())
        return;

    // #i46898# no wizards if there is no Base installed - currently, all wizards are
    // database related
    if (!SvtModuleOptions().IsDataBaseInstalled())
        return;

    if ( m_nControlWizardEvent )
        Application::RemoveUserEvent( m_nControlWizardEvent );
    m_nControlWizardEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnStartControlWizard ) );
}

void FmXFormView::breakCreateFormObject()
{
    if (m_nControlWizardEvent != nullptr)
    {
        Application::RemoveUserEvent(m_nControlWizardEvent);
        m_nControlWizardEvent = nullptr;
    }
    m_xLastCreatedControlModel.clear();
}

Reference<XWindow> FmXFormView::GetParentWindow() const
{
    const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
    const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
    return VCLUnoHelper::GetInterface(const_cast<vcl::Window*>(pCurrentWindow));
}

IMPL_LINK_NOARG( FmXFormView, OnStartControlWizard, void*, void )
{
    m_nControlWizardEvent = nullptr;
    OSL_PRECOND( m_xLastCreatedControlModel.is(), "FmXFormView::OnStartControlWizard: illegal call!" );
    if ( !m_xLastCreatedControlModel.is() )
        return;

    sal_Int16 nClassId = FormComponentType::CONTROL;
    try
    {
        OSL_VERIFY( m_xLastCreatedControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }

    const char* pWizardAsciiName = nullptr;
    switch ( nClassId )
    {
        case FormComponentType::GRIDCONTROL:
            pWizardAsciiName = "com.sun.star.sdb.GridControlAutoPilot";
            break;
        case FormComponentType::LISTBOX:
        case FormComponentType::COMBOBOX:
            pWizardAsciiName = "com.sun.star.sdb.ListComboBoxAutoPilot";
            break;
        case FormComponentType::GROUPBOX:
            pWizardAsciiName = "com.sun.star.sdb.GroupBoxAutoPilot";
            break;
    }

    if ( pWizardAsciiName )
    {
        // build the argument list
        ::comphelper::NamedValueCollection aWizardArgs;
        aWizardArgs.put(u"ObjectModel"_ustr, m_xLastCreatedControlModel);
        aWizardArgs.put(u"ParentWindow"_ustr, GetParentWindow());

        // create the wizard object
        Reference< XExecutableDialog > xWizard;
        try
        {
            const Reference<XComponentContext>& xContext = comphelper::getProcessComponentContext();
            xWizard.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii(pWizardAsciiName), aWizardArgs.getWrappedPropertyValues(), xContext ), UNO_QUERY);
        }
        catch (const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }

        if ( !xWizard.is() )
        {
            ShowServiceNotAvailableError( nullptr, OUString::createFromAscii(pWizardAsciiName), true );
        }
        else
        {
            // execute the wizard
            try
            {
                xWizard->execute();
            }
            catch (const Exception&)
            {
                DBG_UNHANDLED_EXCEPTION("svx");
            }
        }
    }

    m_xLastCreatedControlModel.clear();
}


namespace
{
    void lcl_insertIntoFormComponentHierarchy_throw( const FmFormView& _rView, const SdrUnoObj& _rSdrObj,
        const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
        const OUString& _rCommand, const sal_Int32 _nCommandType )
    {
        FmFormPage& rPage = static_cast< FmFormPage& >( *_rView.GetSdrPageView()->GetPage() );

        Reference< XFormComponent > xFormComponent( _rSdrObj.GetUnoControlModel(), UNO_QUERY_THROW );
        Reference< XForm > xTargetForm(
            rPage.GetImpl().findPlaceInFormComponentHierarchy( xFormComponent, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType ),
            UNO_SET_THROW );

        FmFormPageImpl::setUniqueName( xFormComponent, xTargetForm );

        Reference< XIndexContainer > xFormAsContainer( xTargetForm, UNO_QUERY_THROW );
        xFormAsContainer->insertByIndex( xFormAsContainer->getCount(), Any( xFormComponent ) );
    }
}


rtl::Reference<SdrObject> FmXFormView::implCreateFieldControl( const svx::ODataAccessDescriptor& _rColumnDescriptor )
{
    // not if we're in design mode
    if ( !m_pView->IsDesignMode() )
        return nullptr;

    OUString sCommand, sFieldName;
    sal_Int32 nCommandType = CommandType::COMMAND;
    SharedConnection xConnection;

    OUString sDataSource = _rColumnDescriptor.getDataSource();
    _rColumnDescriptor[ DataAccessDescriptorProperty::Command ]     >>= sCommand;
    _rColumnDescriptor[ DataAccessDescriptorProperty::ColumnName ]  >>= sFieldName;
    _rColumnDescriptor[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType;
    {
        Reference< XConnection > xExternalConnection;
        _rColumnDescriptor[ DataAccessDescriptorProperty::Connection ]  >>= xExternalConnection;
        xConnection.reset( xExternalConnection, SharedConnection::NoTakeOwnership );
    }

    if  (   sCommand.isEmpty()
        ||  sFieldName.isEmpty()
        ||  (   sDataSource.isEmpty()
            &&  !xConnection.is()
            )
        )
    {
        OSL_FAIL( "FmXFormView::implCreateFieldControl: nonsense!" );
    }

    Reference< XDataSource > xDataSource;
    SQLErrorEvent aError;
    try
    {
        if ( xConnection.is() && !xDataSource.is() && sDataSource.isEmpty() )
        {
            Reference< XChild > xChild( xConnection, UNO_QUERY );
            if ( xChild.is() )
                xDataSource.set(xChild->getParent(), css::uno::UNO_QUERY);
        }

        // obtain the data source
        if ( !xDataSource.is() )
            xDataSource = getDataSource( sDataSource, comphelper::getProcessComponentContext() );

        // and the connection, if necessary
        if ( !xConnection.is() )
            xConnection.reset( getConnection_withFeedback(
                sDataSource,
                OUString(),
                OUString(),
                comphelper::getProcessComponentContext(),
                nullptr
            ) );
    }
    catch (const SQLException&)
    {
        aError.Reason = ::cppu::getCaughtException();
    }
    catch (const Exception& )
    {
        /* will be asserted below */
    }
    if (aError.Reason.hasValue())
    {
        displayAsyncErrorMessage( aError );
        return nullptr;
    }

    // need a data source and a connection here
    if (!xDataSource.is() || !xConnection.is())
    {
        OSL_FAIL("FmXFormView::implCreateFieldControl : could not retrieve the data source or the connection!");
        return nullptr;
    }

    Reference< XComponent > xKeepFieldsAlive;
    // go
    try
    {
        // determine the table/query field which we should create a control for
        Reference< XPropertySet >   xField;

        Reference< XNameAccess >    xFields = getFieldsByCommandDescriptor(
            xConnection, nCommandType, sCommand, xKeepFieldsAlive );

        if (xFields.is() && xFields->hasByName(sFieldName))
            xFields->getByName(sFieldName) >>= xField;
        if ( !xField.is() )
            return nullptr;

        Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( xConnection ), UNO_SET_THROW );
        Reference< XNumberFormats >  xNumberFormats( xSupplier->getNumberFormats(), UNO_SET_THROW );

        OUString sLabelPostfix;


        // only for text size
        OutputDevice* pOutDev = nullptr;
        if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
            pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
        else
        {// find OutDev
            if (SdrPageView* pPageView = m_pView->GetSdrPageView())
            {
                // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
                // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();

                for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);

                    if( rPageWindow.GetPaintWindow().OutputToWindow())
                    {
                        pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
                        break;
                    }
                }
            }
        }

        if ( !pOutDev )
            return nullptr;

        sal_Int32 nDataType = ::comphelper::getINT32(xField->getPropertyValue(FM_PROP_FIELDTYPE));
        if ((DataType::BINARY == nDataType) || (DataType::VARBINARY == nDataType))
            return nullptr;


        // determine the control type by examining the data type of the bound column
        SdrObjKind nOBJID = SdrObjKind::NONE;
        bool bDateNTimeField = false;

        bool bIsCurrency = false;
        if (::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField))
            bIsCurrency = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY));

        if (bIsCurrency)
            nOBJID = SdrObjKind::FormCurrencyField;
        else
            switch (nDataType)
            {
                case DataType::BLOB:
                case DataType::LONGVARBINARY:
                    nOBJID = SdrObjKind::FormImageControl;
                    break;
                case DataType::LONGVARCHAR:
                case DataType::CLOB:
                    nOBJID = SdrObjKind::FormEdit;
                    break;
                case DataType::BINARY:
                case DataType::VARBINARY:
                    return nullptr;
                case DataType::BIT:
                case DataType::BOOLEAN:
                    nOBJID = SdrObjKind::FormCheckbox;
                    break;
                case DataType::TINYINT:
                case DataType::SMALLINT:
                case DataType::INTEGER:
                    nOBJID = SdrObjKind::FormNumericField;
                    break;
                case DataType::REAL:
                case DataType::DOUBLE:
                case DataType::NUMERIC:
                case DataType::DECIMAL:
                    nOBJID = SdrObjKind::FormFormattedField;
                    break;
                case DataType::TIMESTAMP:
                    bDateNTimeField = true;
                    sLabelPostfix = SvxResId(RID_STR_POSTFIX_DATE);
                    [[fallthrough]];
                case DataType::DATE:
                    nOBJID = SdrObjKind::FormDateField;
                    break;
                case DataType::TIME:
                    nOBJID = SdrObjKind::FormTimeField;
                    break;
                case DataType::CHAR:
                case DataType::VARCHAR:
                default:
                    nOBJID = SdrObjKind::FormEdit;
                    break;
            }
        if (nOBJID == SdrObjKind::NONE)
            return nullptr;

        rtl::Reference<SdrUnoObj> pLabel;
        rtl::Reference<SdrUnoObj> pControl;
        if  (   !createControlLabelPair( *pOutDev, 0, 0, xField, xNumberFormats, nOBJID, sLabelPostfix,
                    pLabel, pControl, xDataSource, sDataSource, sCommand, nCommandType )
            )
        {
            return nullptr;
        }


        // group objects
        bool bCheckbox = ( SdrObjKind::FormCheckbox == nOBJID );
        OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateFieldControl: why was there a label created for a check box?" );
        if ( bCheckbox )
            return pControl;

        rtl::Reference<SdrObjGroup> pGroup  = new SdrObjGroup(getView()->getSdrModelFromSdrView());
        SdrObjList* pObjList = pGroup->GetSubList();
        pObjList->InsertObject( pLabel.get() );
        pObjList->InsertObject( pControl.get() );

        if ( bDateNTimeField )
        {   // so far we created a date field only, but we also need a time field
            if  (   createControlLabelPair( *pOutDev, 0, 1000, xField, xNumberFormats, SdrObjKind::FormTimeField,
                        SvxResId(RID_STR_POSTFIX_TIME), pLabel, pControl,
                        xDataSource, sDataSource, sCommand, nCommandType )
                )
            {
                pObjList->InsertObject( pLabel.get() );
                pObjList->InsertObject( pControl.get() );
            }
        }

        return pGroup; // and done
    }
    catch (const Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }


    return nullptr;
}


rtl::Reference<SdrObject> FmXFormView::implCreateXFormsControl( const svx::OXFormsDescriptor &_rDesc )
{
    // not if we're in design mode
    if ( !m_pView->IsDesignMode() )
        return nullptr;

    // go
    try
    {
        // determine the table/query field which we should create a control for
        Reference< XNumberFormats > xNumberFormats;
        OUString sLabelPostfix = _rDesc.szName;


        // only for text size
        OutputDevice* pOutDev = nullptr;
        if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
            pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
        else
        {// find OutDev
            if (SdrPageView* pPageView = m_pView->GetSdrPageView())
            {
                // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
                // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();

                for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
                {
                    const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);

                    if( rPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType() == OUTDEV_WINDOW)
                    {
                        pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
                        break;
                    }
                }
            }
        }

        if ( !pOutDev )
            return nullptr;


        // The service name decides which control should be created
        SdrObjKind nOBJID = SdrObjKind::FormEdit;
        if(_rDesc.szServiceName == FM_SUN_COMPONENT_NUMERICFIELD)
            nOBJID = SdrObjKind::FormNumericField;
        if(_rDesc.szServiceName == FM_SUN_COMPONENT_CHECKBOX)
            nOBJID = SdrObjKind::FormCheckbox;
        if(_rDesc.szServiceName == FM_COMPONENT_COMMANDBUTTON)
            nOBJID = SdrObjKind::FormButton;

        Reference< css::form::submission::XSubmission > xSubmission(_rDesc.xPropSet, UNO_QUERY);

        // xform control or submission button?
        if ( !xSubmission.is() )
        {
            rtl::Reference<SdrUnoObj> pLabel;
            rtl::Reference<SdrUnoObj> pControl;
            if  (   !createControlLabelPair( *pOutDev, 0, 0, nullptr, xNumberFormats, nOBJID, sLabelPostfix,
                        pLabel, pControl, nullptr, u""_ustr, u""_ustr, -1 )
                )
            {
                return nullptr;
            }


            // Now build the connection between the control and the data item.
            Reference< XValueBinding > xValueBinding(_rDesc.xPropSet,UNO_QUERY);
            Reference< XBindableValue > xBindableValue(pControl->GetUnoControlModel(),UNO_QUERY);

            DBG_ASSERT( xBindableValue.is(), "FmXFormView::implCreateXFormsControl: control's not bindable!" );
            if ( xBindableValue.is() )
                xBindableValue->setValueBinding(xValueBinding);

            bool bCheckbox = ( SdrObjKind::FormCheckbox == nOBJID );
            OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateXFormsControl: why was there a label created for a check box?" );
            if ( bCheckbox )
                return pControl;


            // group objects
            rtl::Reference<SdrObjGroup> pGroup  = new SdrObjGroup(getView()->getSdrModelFromSdrView());
            SdrObjList* pObjList = pGroup->GetSubList();
            pObjList->InsertObject(pLabel.get());
            pObjList->InsertObject(pControl.get());

            return pGroup;
        }
        else {

            // create a button control
            const MapMode& eTargetMode( pOutDev->GetMapMode() );
            const MapMode eSourceMode(MapUnit::Map100thMM);
            const SdrObjKind nObjID = SdrObjKind::FormButton;
            ::Size controlSize(4000, 500);
            rtl::Reference<FmFormObj> pControl = static_cast<FmFormObj*>(
                SdrObjFactory::MakeNewObject(
                    getView()->getSdrModelFromSdrView(),
                    SdrInventor::FmForm,
                    nObjID).get());
            controlSize.setWidth( tools::Long(controlSize.Width() * eTargetMode.GetScaleX()) );
            controlSize.setHeight( tools::Long(controlSize.Height() * eTargetMode.GetScaleY()) );
            ::Point controlPos( OutputDevice::LogicToLogic( ::Point( controlSize.Width(), 0 ), eSourceMode, eTargetMode ) );
            ::tools::Rectangle controlRect( controlPos, OutputDevice::LogicToLogic( controlSize, eSourceMode, eTargetMode ) );
            pControl->SetLogicRect(controlRect);

            // set the button label
            Reference< XPropertySet > xControlSet(pControl->GetUnoControlModel(), UNO_QUERY);
            xControlSet->setPropertyValue(FM_PROP_LABEL, Any(_rDesc.szName));

            // connect the submission with the submission supplier (aka the button)
            xControlSet->setPropertyValue( FM_PROP_BUTTON_TYPE,
                                           Any( FormButtonType_SUBMIT ) );
            Reference< css::form::submission::XSubmissionSupplier > xSubmissionSupplier(pControl->GetUnoControlModel(), UNO_QUERY);
            xSubmissionSupplier->setSubmission(xSubmission);

            return rtl::Reference<SdrObject>(pControl);
        }
    }
    catch (const Exception&)
    {
        TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while creating the control !");
    }


    return nullptr;
}

bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
        const Reference< XPropertySet >& _rxField, const Reference< XNumberFormats >& _rxNumberFormats,
        SdrObjKind _nControlObjectID, std::u16string_view _rFieldPostfix,
        rtl::Reference<SdrUnoObj>& _rpLabel,
        rtl::Reference<SdrUnoObj>& _rpControl,
        const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
        const OUString& _rCommand, const sal_Int32 _nCommandType )
{
    if(!createControlLabelPair(
        _rOutDev,
        _nXOffsetMM,
        _nYOffsetMM,
        _rxField,
        _rxNumberFormats,
        _nControlObjectID,
        _rFieldPostfix,
        SdrInventor::FmForm,
        SdrObjKind::FormFixedText,

        // tdf#118963 Hand over a SdrModel to SdrObject-creation. It uses the local m_pView
        // and already returning false when nullptr == getView() could be done, but m_pView
        // is already dereferenced here in many places (see below), so just use it for now.
        getView()->getSdrModelFromSdrView(),

        _rpLabel,
        _rpControl))
    {
        return false;
    }

    // insert the control model(s) into the form component hierarchy
    if ( _rpLabel )
        lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpLabel, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
    lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpControl, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );

    // some context-dependent initializations
    FormControlFactory aControlFactory;
    if ( _rpLabel )
        aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpLabel );
    aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpControl );

    return true;
}


bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
    const Reference< XPropertySet >& _rxField,
    const Reference< XNumberFormats >& _rxNumberFormats, SdrObjKind _nControlObjectID,
    std::u16string_view _rFieldPostfix, SdrInventor _nInventor, SdrObjKind _nLabelObjectID,
    SdrModel& _rModel,
    rtl::Reference<SdrUnoObj>& _rpLabel, rtl::Reference<SdrUnoObj>& _rpControl)
{
    sal_Int32 nDataType = 0;
    OUString sFieldName;
    Any aFieldName;
    if ( _rxField.is() )
    {
        nDataType = ::comphelper::getINT32(_rxField->getPropertyValue(FM_PROP_FIELDTYPE));
        aFieldName = _rxField->getPropertyValue(FM_PROP_NAME);
        aFieldName >>= sFieldName;
    }

    // calculate the positions, respecting the settings of the target device
    ::Size aTextSize( _rOutDev.GetTextWidth(sFieldName + _rFieldPostfix), _rOutDev.GetTextHeight() );

    const MapMode & eTargetMode( _rOutDev.GetMapMode() );
    MapMode eSourceMode( MapUnit::Map100thMM );

    // text width is at least 4 centimeters
    // text height is always half a centimeter
    ::Size aDefTxtSize(4000, 500);
    ::Size aDefSize(4000, 500);
    ::Size aDefImageSize(4000, 4000);

    ::Size aRealSize = OutputDevice::LogicToLogic(aTextSize, eTargetMode, eSourceMode);
    aRealSize.setWidth( std::max(aRealSize.Width(), aDefTxtSize.Width()) );
    aRealSize.setHeight( aDefSize.Height() );

    // adjust to scaling of the target device (#53523#)
    aRealSize.setWidth( tools::Long(Fraction(aRealSize.Width(), 1) * eTargetMode.GetScaleX()) );
    aRealSize.setHeight( tools::Long(Fraction(aRealSize.Height(), 1) * eTargetMode.GetScaleY()) );

    // for boolean fields, we do not create a label, but just a checkbox
    bool bNeedLabel = ( _nControlObjectID != SdrObjKind::FormCheckbox );

    // the label
    rtl::Reference< SdrUnoObj > pLabel;
    Reference< XPropertySet > xLabelModel;

    if ( bNeedLabel )
    {
        pLabel = dynamic_cast< SdrUnoObj* >(
            SdrObjFactory::MakeNewObject(
                _rModel,
                _nInventor,
                _nLabelObjectID).get() );

        OSL_ENSURE(pLabel, "FmXFormView::createControlLabelPair: could not create the label!");

        if (!pLabel)
            return false;

        xLabelModel.set( pLabel->GetUnoControlModel(), UNO_QUERY );
        if ( xLabelModel.is() )
        {
            OUString sLabel;
            if ( _rxField.is() && _rxField->getPropertySetInfo()->hasPropertyByName(FM_PROP_LABEL) )
                _rxField->getPropertyValue(FM_PROP_LABEL) >>= sLabel;
            if ( sLabel.isEmpty() )
                sLabel = sFieldName;

            xLabelModel->setPropertyValue( FM_PROP_LABEL, Any( sLabel + _rFieldPostfix ) );
            OUString sObjectLabel(SvxResId(RID_STR_OBJECT_LABEL).replaceAll("#object#", sFieldName));
            xLabelModel->setPropertyValue(FM_PROP_NAME, Any(sObjectLabel));
        }

        pLabel->SetLogicRect( ::tools::Rectangle(
            OutputDevice::LogicToLogic( ::Point( _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
            OutputDevice::LogicToLogic( aRealSize, eSourceMode, eTargetMode )
        ) );
    }

    // the control
    rtl::Reference< SdrUnoObj > pControl( dynamic_cast< SdrUnoObj* >(
        SdrObjFactory::MakeNewObject(
            _rModel,
             _nInventor,
             _nControlObjectID).get() ));

    OSL_ENSURE(pControl, "FmXFormView::createControlLabelPair: could not create the control!");

    if (!pControl)
        return false;

    Reference< XPropertySet > xControlSet( pControl->GetUnoControlModel(), UNO_QUERY );
    if ( !xControlSet.is() )
        return false;

    // size of the control
    ::Size aControlSize( aDefSize );
    switch ( nDataType )
    {
    case DataType::BIT:
    case DataType::BOOLEAN:
        aControlSize = aDefSize;
        break;
    case DataType::LONGVARCHAR:
    case DataType::CLOB:
    case DataType::LONGVARBINARY:
    case DataType::BLOB:
        aControlSize = aDefImageSize;
        break;
    }

    if ( SdrObjKind::FormImageControl == _nControlObjectID )
        aControlSize = aDefImageSize;

    aControlSize.setWidth( tools::Long(Fraction(aControlSize.Width(), 1) * eTargetMode.GetScaleX()) );
    aControlSize.setHeight( tools::Long(Fraction(aControlSize.Height(), 1) * eTargetMode.GetScaleY()) );

    pControl->SetLogicRect( ::tools::Rectangle(
        OutputDevice::LogicToLogic( ::Point( aRealSize.Width() + _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
        OutputDevice::LogicToLogic( aControlSize, eSourceMode, eTargetMode )
    ) );

    // some initializations
    Reference< XPropertySetInfo > xControlPropInfo = xControlSet->getPropertySetInfo();

    if ( aFieldName.hasValue() )
    {
        xControlSet->setPropertyValue( FM_PROP_CONTROLSOURCE, aFieldName );
        xControlSet->setPropertyValue( FM_PROP_NAME, aFieldName );
        if ( !bNeedLabel )
        {
            // no dedicated label control => use the label property
            if ( xControlPropInfo->hasPropertyByName( FM_PROP_LABEL ) )
                xControlSet->setPropertyValue( FM_PROP_LABEL, Any( sFieldName + _rFieldPostfix ) );
            else
                OSL_FAIL( "FmXFormView::createControlLabelPair: can't set a label for the control!" );
        }
    }

    if ( (nDataType == DataType::LONGVARCHAR || nDataType == DataType::CLOB) && xControlPropInfo->hasPropertyByName( FM_PROP_MULTILINE ) )
    {
        xControlSet->setPropertyValue( FM_PROP_MULTILINE, Any( true ) );
    }

    // announce the label to the control
    if ( xControlPropInfo->hasPropertyByName( FM_PROP_CONTROLLABEL ) && xLabelModel.is() )
    {
        try
        {
            xControlSet->setPropertyValue( FM_PROP_CONTROLLABEL, Any( xLabelModel ) );
        }
        catch (const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
    }

    if ( _rxField.is() )
    {
        FormControlFactory::initializeFieldDependentProperties( _rxField, xControlSet, _rxNumberFormats );
    }

    _rpLabel = std::move(pLabel);
    _rpControl = std::move(pControl);
    return true;
}


FmXFormView::ObjectRemoveListener::ObjectRemoveListener( FmXFormView* pParent )
    :m_pParent( pParent )
{
}


void FmXFormView::ObjectRemoveListener::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
        return;
    const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
    if (pSdrHint->GetKind() == SdrHintKind::ObjectRemoved)
        m_pParent->ObjectRemovedInAliveMode(pSdrHint->GetObject());
}


void FmXFormView::ObjectRemovedInAliveMode( const SdrObject* pObject )
{
    // if the remote object in my MarkList, which I have memorized when switching to the
    // Alive mode, I have to take it out now, because I otherwise try to set the mark
    // again when switching back (interestingly, this fails only with grouped objects
    // (when accessing their ObjList GPF), not with individual ones)

    const size_t nCount = m_aMark.GetMarkCount();
    for (size_t i = 0; i < nCount; ++i)
    {
        SdrMark* pMark = m_aMark.GetMark(i);
        SdrObject* pCurrent = pMark->GetMarkedSdrObj();
        if (pObject == pCurrent)
        {
            m_aMark.DeleteMark(i);
            return;
        }
        // I do not need to descend into GroupObjects: if an object is deleted there,
        // then the pointer, which I have, to the GroupObject still remains valid ...
    }
}


void FmXFormView::stopMarkListWatching()
{
    if ( m_pWatchStoredList )
    {
        m_pWatchStoredList->EndListeningAll();
        m_pWatchStoredList.reset();
    }
}


void FmXFormView::startMarkListWatching()
{
    if ( !m_pWatchStoredList )
    {
        FmFormModel* pModel = GetFormShell() ? GetFormShell()->GetFormModel() : nullptr;
        DBG_ASSERT( pModel != nullptr, "FmXFormView::startMarkListWatching: shell has no model!" );
        if (pModel)
        {
            m_pWatchStoredList.reset(new ObjectRemoveListener( this ));
            m_pWatchStoredList->StartListening( *static_cast< SfxBroadcaster* >( pModel ) );
        }
    }
    else
    {
        OSL_FAIL( "FmXFormView::startMarkListWatching: already listening!" );
    }
}

void FmXFormView::saveMarkList()
{
    if ( m_pView )
    {
        m_aMark = m_pView->GetMarkedObjectList();
        const size_t nCount = m_aMark.GetMarkCount( );
        for ( size_t i = 0; i < nCount; ++i )
        {
            SdrMark*   pMark = m_aMark.GetMark(i);
            SdrObject* pObj  = pMark->GetMarkedSdrObj();

            if ( m_pView->IsObjMarked( pObj ) )
            {
                if ( pObj->IsGroupObject() )
                {
                    SdrObjListIter aIter( pObj->GetSubList() );
                    bool bMixed = false;
                    while ( aIter.IsMore() && !bMixed )
                        bMixed = ( aIter.Next()->GetObjInventor() != SdrInventor::FmForm );

                    if ( !bMixed )
                    {
                        // all objects in the group are form objects
                        m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
                    }
                }
                else
                {
                    if ( pObj->GetObjInventor() == SdrInventor::FmForm )
                    {   // this is a form layer object
                        m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
                    }
                }
            }
        }
    }
    else
    {
        OSL_FAIL( "FmXFormView::saveMarkList: invalid view!" );
        m_aMark.Clear();
    }
}

static bool lcl_hasObject( SdrObjListIter& rIter, SdrObject const * pObj )
{
    bool bFound = false;
    while (rIter.IsMore() && !bFound)
        bFound = pObj == rIter.Next();

    rIter.Reset();
    return bFound;
}


void FmXFormView::restoreMarkList( SdrMarkList& _rRestoredMarkList )
{
    if ( !m_pView )
        return;

    _rRestoredMarkList.Clear();

    const SdrMarkList& rCurrentList = m_pView->GetMarkedObjectList();
    FmFormPage* pPage = GetFormShell() ? GetFormShell()->GetCurPage() : nullptr;
    if (!pPage)
        return;

    if (rCurrentList.GetMarkCount())
    {   // there is a current mark ... hmm. Is it a subset of the mark we remembered in saveMarkList?
        bool bMisMatch = false;

        // loop through all current marks
        const size_t nCurrentCount = rCurrentList.GetMarkCount();
        for ( size_t i=0; i<nCurrentCount && !bMisMatch; ++i )
        {
            const SdrObject* pCurrentMarked = rCurrentList.GetMark( i )->GetMarkedSdrObj();

            // loop through all saved marks, check for equality
            bool bFound = false;
            const size_t nSavedCount = m_aMark.GetMarkCount();
            for ( size_t j=0; j<nSavedCount && !bFound; ++j )
            {
                if ( m_aMark.GetMark( j )->GetMarkedSdrObj() == pCurrentMarked )
                    bFound = true;
            }

            // did not find a current mark in the saved marks
            if ( !bFound )
                bMisMatch = true;
        }

        if ( bMisMatch )
        {
            m_aMark.Clear();
            _rRestoredMarkList = rCurrentList;
            return;
        }
    }
    // it is important that the objects of the mark list are not accessed,
    // because they can be already destroyed
    SdrPageView* pCurPageView = m_pView->GetSdrPageView();
    SdrObjListIter aPageIter( pPage );
    bool bFound = true;

    // do all objects still exist
    const size_t nCount = m_aMark.GetMarkCount();
    for (size_t i = 0; i < nCount && bFound; ++i)
    {
        SdrMark*   pMark = m_aMark.GetMark(i);
        SdrObject* pObj  = pMark->GetMarkedSdrObj();
        if (pObj->IsGroupObject())
        {
            SdrObjListIter aIter(pObj->GetSubList());
            while (aIter.IsMore() && bFound)
                bFound = lcl_hasObject(aPageIter, aIter.Next());
        }
        else
            bFound = lcl_hasObject(aPageIter, pObj);

        bFound = bFound && pCurPageView == pMark->GetPageView();
    }

    if (bFound)
    {
        // evaluate the LastObject
        if (nCount) // now mark the objects
        {
            for (size_t i = 0; i < nCount; ++i)
            {
                SdrMark* pMark = m_aMark.GetMark(i);
                SdrObject* pObj = pMark->GetMarkedSdrObj();
                if ( pObj->GetObjInventor() == SdrInventor::FmForm )
                    if ( !m_pView->IsObjMarked( pObj ) )
                        m_pView->MarkObj( pObj, pMark->GetPageView() );
            }

            _rRestoredMarkList = m_aMark;
        }
    }
    m_aMark.Clear();
}

void SAL_CALL FmXFormView::focusGained( const FocusEvent& /*e*/ )
{
    if ( m_xWindow.is() && m_pView )
    {
        m_pView->SetMoveOutside( true, FmFormView::ImplAccess() );
    }
}

void SAL_CALL FmXFormView::focusLost( const FocusEvent& /*e*/ )
{
    // when switch the focus outside the office the mark didn't change
    // so we can not remove us as focus listener
    if ( m_xWindow.is() && m_pView )
    {
        m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
    }
}

DocumentType FmXFormView::impl_getDocumentType() const
{
    if ( GetFormShell() && GetFormShell()->GetImpl() )
        return GetFormShell()->GetImpl()->getDocumentType_Lock();
    return eUnknownDocumentType;
}

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