diff options
Diffstat (limited to 'svx/source/form/fmvwimp.cxx')
-rw-r--r-- | svx/source/form/fmvwimp.cxx | 1918 |
1 files changed, 1918 insertions, 0 deletions
diff --git a/svx/source/form/fmvwimp.cxx b/svx/source/form/fmvwimp.cxx new file mode 100644 index 000000000..c95c5d710 --- /dev/null +++ b/svx/source/form/fmvwimp.cxx @@ -0,0 +1,1918 @@ +/* -*- 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/sdb/XQueriesSupplier.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 <tools/diagnose_ex.h> +#include <sal/log.hxx> +#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( const css::uno::Reference<css::uno::XComponentContext>& _rContext, const SdrPageWindow& _rWindow, FmXFormView* _pViewImpl ) +: m_xControlContainer( _rWindow.GetControlContainer() ), + m_xContext( _rContext ), + 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 Reference< XControlContainer >& 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 + Reference< XContainer > xContainer( xCC, UNO_QUERY ); + if ( xContainer.is() ) + xContainer->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 = 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().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) ) + 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("ObjectModel", m_xLastCreatedControlModel); + aWizardArgs.put("ParentWindow", GetParentWindow()); + + // create the wizard object + Reference< XExecutableDialog > xWizard; + try + { + 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 ) ); + } +} + + +SdrObjectUniquePtr 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; + + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pLabel; + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> 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 SdrObjectUniquePtr(pControl.release()); + + SdrObjGroup* pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView()); + SdrObjList* pObjList = pGroup->GetSubList(); + pObjList->InsertObject( pLabel.release() ); + pObjList->InsertObject( pControl.release() ); + + 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.release() ); + pObjList->InsertObject( pControl.release() ); + } + } + + return SdrObjectUniquePtr(pGroup); // and done + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + + + return nullptr; +} + + +SdrObjectUniquePtr 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() ) + { + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pLabel; + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pControl; + if ( !createControlLabelPair( *pOutDev, 0, 0, nullptr, xNumberFormats, nOBJID, sLabelPostfix, + pLabel, pControl, nullptr, "", "", -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 SdrObjectUniquePtr(pControl.release()); + + + // group objects + SdrObjGroup* pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView()); + SdrObjList* pObjList = pGroup->GetSubList(); + pObjList->InsertObject(pLabel.release()); + pObjList->InsertObject(pControl.release()); + + return SdrObjectUniquePtr(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); + FmFormObj *pControl = static_cast<FmFormObj*>( + SdrObjFactory::MakeNewObject( + getView()->getSdrModelFromSdrView(), + SdrInventor::FmForm, + nObjID)); + 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 SdrObjectUniquePtr(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, + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel, + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _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, + std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel, std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _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() ); + + MapMode eTargetMode( _rOutDev.GetMapMode() ), + 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 + ::std::unique_ptr< SdrUnoObj, SdrObjectFreeOp > pLabel; + Reference< XPropertySet > xLabelModel; + + if ( bNeedLabel ) + { + pLabel.reset( dynamic_cast< SdrUnoObj* >( + SdrObjFactory::MakeNewObject( + _rModel, + _nInventor, + _nLabelObjectID))); + + 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 + ::std::unique_ptr< SdrUnoObj, SdrObjectFreeOp > pControl( dynamic_cast< SdrUnoObj* >( + SdrObjFactory::MakeNewObject( + _rModel, + _nInventor, + _nControlObjectID))); + + 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: */ |