/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::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 _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::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( 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( 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( 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 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(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 { Reference 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 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(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 pLabel; rtl::Reference 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 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 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(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 pLabel; rtl::Reference 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 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 pControl = static_cast( 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(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& _rpLabel, rtl::Reference& _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& _rpLabel, rtl::Reference& _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 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(&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; iGetMarkedSdrObj(); // loop through all saved marks, check for equality bool bFound = false; const size_t nSavedCount = m_aMark.GetMarkCount(); for ( size_t j=0; jGetMarkedSdrObj() == 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: */