bump product version to 7.2.5.1
[LibreOffice.git] / svx / source / form / fmvwimp.cxx
blob790b41ce25b50c4cbdaae91d2e87e119284929e1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <memory>
22 #include <fmdocumentclassification.hxx>
23 #include <fmobj.hxx>
24 #include <fmpgeimp.hxx>
25 #include <fmprop.hxx>
26 #include <svx/strings.hrc>
27 #include <fmservs.hxx>
28 #include <fmshimp.hxx>
29 #include <svx/fmtools.hxx>
30 #include <fmvwimp.hxx>
31 #include <formcontrolfactory.hxx>
32 #include <svx/sdrpaintwindow.hxx>
33 #include <svx/svditer.hxx>
34 #include <svx/dataaccessdescriptor.hxx>
35 #include <svx/dialmgr.hxx>
36 #include <svx/svdobjkind.hxx>
37 #include <svx/fmmodel.hxx>
38 #include <svx/fmpage.hxx>
39 #include <svx/fmshell.hxx>
40 #include <svx/fmview.hxx>
41 #include <svx/sdrpagewindow.hxx>
42 #include <svx/svdogrp.hxx>
43 #include <svx/svdpagv.hxx>
44 #include <svx/xmlexchg.hxx>
45 #include <toolkit/helper/vclunohelper.hxx>
47 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
48 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
49 #include <com/sun/star/sdbc/XRowSet.hpp>
50 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
51 #include <com/sun/star/util/XNumberFormats.hpp>
52 #include <com/sun/star/sdb/CommandType.hpp>
53 #include <com/sun/star/sdbc/DataType.hpp>
54 #include <com/sun/star/form/FormComponentType.hpp>
55 #include <com/sun/star/form/FormButtonType.hpp>
56 #include <com/sun/star/form/binding/XBindableValue.hpp>
57 #include <com/sun/star/form/binding/XValueBinding.hpp>
58 #include <com/sun/star/form/runtime/FormController.hpp>
59 #include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
60 #include <com/sun/star/awt/XTabControllerModel.hpp>
61 #include <com/sun/star/awt/XControlContainer.hpp>
62 #include <com/sun/star/awt/XTabController.hpp>
63 #include <com/sun/star/container/XIndexAccess.hpp>
64 #include <com/sun/star/awt/XControl.hpp>
65 #include <com/sun/star/sdbc/SQLException.hpp>
66 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
67 #include <com/sun/star/container/XContainer.hpp>
69 #include <comphelper/namedvaluecollection.hxx>
70 #include <comphelper/property.hxx>
71 #include <comphelper/processfactory.hxx>
72 #include <comphelper/types.hxx>
73 #include <cppuhelper/exc_hlp.hxx>
74 #include <unotools/moduleoptions.hxx>
75 #include <tools/debug.hxx>
76 #include <tools/diagnose_ex.h>
77 #include <sal/log.hxx>
78 #include <vcl/svapp.hxx>
79 #include <vcl/stdtext.hxx>
80 #include <vcl/window.hxx>
81 #include <connectivity/dbtools.hxx>
83 #include <algorithm>
85 using namespace ::comphelper;
86 using namespace ::svx;
87 using namespace ::svxform;
88 using namespace ::dbtools;
90 using namespace ::com::sun::star;
91 using ::com::sun::star::uno::Exception;
92 using ::com::sun::star::uno::XInterface;
93 using ::com::sun::star::uno::Sequence;
94 using ::com::sun::star::uno::UNO_QUERY;
95 using ::com::sun::star::uno::UNO_QUERY_THROW;
96 using ::com::sun::star::uno::UNO_SET_THROW;
97 using ::com::sun::star::uno::Type;
98 using ::com::sun::star::uno::Reference;
99 using ::com::sun::star::uno::Any;
100 using ::com::sun::star::uno::makeAny;
101 using ::com::sun::star::uno::XComponentContext;
102 using ::com::sun::star::form::FormButtonType_SUBMIT;
103 using ::com::sun::star::form::binding::XValueBinding;
104 using ::com::sun::star::form::binding::XBindableValue;
105 using ::com::sun::star::lang::XComponent;
106 using ::com::sun::star::container::XIndexAccess;
107 using ::com::sun::star::form::runtime::FormController;
108 using ::com::sun::star::form::runtime::XFormController;
109 using ::com::sun::star::script::XEventAttacherManager;
110 using ::com::sun::star::awt::XTabControllerModel;
111 using ::com::sun::star::container::XChild;
112 using ::com::sun::star::task::XInteractionHandler;
113 using ::com::sun::star::awt::XTabController;
114 using ::com::sun::star::awt::XControlContainer;
115 using ::com::sun::star::awt::XControl;
116 using ::com::sun::star::form::XFormComponent;
117 using ::com::sun::star::form::XForm;
118 using ::com::sun::star::lang::IndexOutOfBoundsException;
119 using ::com::sun::star::container::XContainer;
120 using ::com::sun::star::container::ContainerEvent;
121 using ::com::sun::star::lang::EventObject;
122 using ::com::sun::star::sdb::SQLErrorEvent;
123 using ::com::sun::star::sdbc::XRowSet;
124 using ::com::sun::star::beans::XPropertySet;
125 using ::com::sun::star::container::XElementAccess;
126 using ::com::sun::star::awt::XWindow;
127 using ::com::sun::star::awt::FocusEvent;
128 using ::com::sun::star::ui::dialogs::XExecutableDialog;
129 using ::com::sun::star::sdbc::XDataSource;
130 using ::com::sun::star::container::XIndexContainer;
131 using ::com::sun::star::sdbc::XConnection;
132 using ::com::sun::star::container::XNameAccess;
133 using ::com::sun::star::sdbc::SQLException;
134 using ::com::sun::star::util::XNumberFormatsSupplier;
135 using ::com::sun::star::util::XNumberFormats;
136 using ::com::sun::star::beans::XPropertySetInfo;
138 namespace FormComponentType = ::com::sun::star::form::FormComponentType;
139 namespace CommandType = ::com::sun::star::sdb::CommandType;
140 namespace DataType = ::com::sun::star::sdbc::DataType;
143 class FmXFormView::ObjectRemoveListener : public SfxListener
145 FmXFormView* m_pParent;
146 public:
147 explicit ObjectRemoveListener( FmXFormView* pParent );
148 virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
151 FormViewPageWindowAdapter::FormViewPageWindowAdapter( const css::uno::Reference<css::uno::XComponentContext>& _rContext, const SdrPageWindow& _rWindow, FmXFormView* _pViewImpl )
152 : m_xControlContainer( _rWindow.GetControlContainer() ),
153 m_xContext( _rContext ),
154 m_pViewImpl( _pViewImpl ),
155 m_pWindow( _rWindow.GetPaintWindow().GetOutputDevice().GetOwnerWindow() )
158 // create an XFormController for every form
159 FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( _rWindow.GetPageView().GetPage() );
160 DBG_ASSERT( pFormPage, "FormViewPageWindowAdapter::FormViewPageWindowAdapter: no FmFormPage found!" );
161 if ( !pFormPage )
162 return;
166 Reference< XIndexAccess > xForms( pFormPage->GetForms(), UNO_QUERY_THROW );
167 sal_uInt32 nLength = xForms->getCount();
168 for (sal_uInt32 i = 0; i < nLength; i++)
170 Reference< XForm > xForm( xForms->getByIndex(i), UNO_QUERY );
171 if ( xForm.is() )
172 setController( xForm, nullptr );
175 catch (const Exception&)
177 DBG_UNHANDLED_EXCEPTION("svx");
181 FormViewPageWindowAdapter::~FormViewPageWindowAdapter()
185 void FormViewPageWindowAdapter::dispose()
187 for ( ::std::vector< Reference< XFormController > >::const_iterator i = m_aControllerList.begin();
188 i != m_aControllerList.end();
194 Reference< XFormController > xController( *i, UNO_SET_THROW );
196 // detaching the events
197 Reference< XChild > xControllerModel( xController->getModel(), UNO_QUERY );
198 if ( xControllerModel.is() )
200 Reference< XEventAttacherManager > xEventManager( xControllerModel->getParent(), UNO_QUERY_THROW );
201 Reference< XInterface > xControllerNormalized( xController, UNO_QUERY_THROW );
202 xEventManager->detach( i - m_aControllerList.begin(), xControllerNormalized );
205 // dispose the formcontroller
206 xController->dispose();
208 catch (const Exception&)
210 DBG_UNHANDLED_EXCEPTION("svx");
214 m_aControllerList.clear();
217 sal_Bool SAL_CALL FormViewPageWindowAdapter::hasElements()
219 return getCount() != 0;
222 Type SAL_CALL FormViewPageWindowAdapter::getElementType()
224 return cppu::UnoType<XFormController>::get();
227 // XIndexAccess
228 sal_Int32 SAL_CALL FormViewPageWindowAdapter::getCount()
230 return m_aControllerList.size();
233 Any SAL_CALL FormViewPageWindowAdapter::getByIndex(sal_Int32 nIndex)
235 if (nIndex < 0 ||
236 nIndex >= getCount())
237 throw IndexOutOfBoundsException();
239 Any aElement;
240 aElement <<= m_aControllerList[nIndex];
241 return aElement;
244 void SAL_CALL FormViewPageWindowAdapter::makeVisible( const Reference< XControl >& Control )
246 SolarMutexGuard aSolarGuard;
248 Reference< XWindow > xWindow( Control, UNO_QUERY );
249 if ( xWindow.is() && m_pViewImpl->getView() && m_pWindow )
251 awt::Rectangle aRect = xWindow->getPosSize();
252 ::tools::Rectangle aNewRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
253 aNewRect = m_pWindow->PixelToLogic( aNewRect );
254 m_pViewImpl->getView()->MakeVisible( aNewRect, *m_pWindow );
258 static Reference< XFormController > getControllerSearchChildren( const Reference< XIndexAccess > & xIndex, const Reference< XTabControllerModel > & xModel)
260 if (xIndex.is() && xIndex->getCount())
262 Reference< XFormController > xController;
264 for (sal_Int32 n = xIndex->getCount(); n-- && !xController.is(); )
266 xIndex->getByIndex(n) >>= xController;
267 if (xModel.get() == xController->getModel().get())
268 return xController;
269 else
271 xController = getControllerSearchChildren(xController, xModel);
272 if ( xController.is() )
273 return xController;
277 return Reference< XFormController > ();
280 // Search the according controller
281 Reference< XFormController > FormViewPageWindowAdapter::getController( const Reference< XForm > & xForm ) const
283 Reference< XTabControllerModel > xModel(xForm, UNO_QUERY);
284 for (const auto& rpController : m_aControllerList)
286 if (rpController->getModel().get() == xModel.get())
287 return rpController;
289 // the current-round controller isn't the right one. perhaps one of its children ?
290 Reference< XFormController > xChildSearch = getControllerSearchChildren(Reference< XIndexAccess > (rpController, UNO_QUERY), xModel);
291 if (xChildSearch.is())
292 return xChildSearch;
294 return Reference< XFormController > ();
298 void FormViewPageWindowAdapter::setController(const Reference< XForm > & xForm, const Reference< XFormController >& _rxParentController )
300 DBG_ASSERT( xForm.is(), "FormViewPageWindowAdapter::setController: there should be a form!" );
301 Reference< XIndexAccess > xFormCps(xForm, UNO_QUERY);
302 if (!xFormCps.is())
303 return;
305 Reference< XTabControllerModel > xTabOrder(xForm, UNO_QUERY);
307 // create a form controller
308 Reference< XFormController > xController( FormController::create(m_xContext) );
310 Reference< XInteractionHandler > xHandler;
311 if ( _rxParentController.is() )
312 xHandler = _rxParentController->getInteractionHandler();
313 else
315 // TODO: should we create a default handler? Not really necessary, since the
316 // FormController itself has a default fallback
318 if ( xHandler.is() )
319 xController->setInteractionHandler( xHandler );
321 xController->setContext( this );
323 xController->setModel( xTabOrder );
324 xController->setContainer( m_xControlContainer );
325 xController->activateTabOrder();
326 xController->addActivateListener( m_pViewImpl );
328 if ( _rxParentController.is() )
329 _rxParentController->addChildController( xController );
330 else
332 m_aControllerList.push_back(xController);
334 xController->setParent( *this );
336 // attaching the events
337 Reference< XEventAttacherManager > xEventManager( xForm->getParent(), UNO_QUERY );
338 xEventManager->attach(m_aControllerList.size() - 1, Reference<XInterface>( xController, UNO_QUERY ), makeAny(xController) );
341 // now go through the subforms
342 sal_uInt32 nLength = xFormCps->getCount();
343 Reference< XForm > xSubForm;
344 for (sal_uInt32 i = 0; i < nLength; i++)
346 if ( xFormCps->getByIndex(i) >>= xSubForm )
347 setController( xSubForm, xController );
352 void FormViewPageWindowAdapter::updateTabOrder( const Reference< XForm >& _rxForm )
354 OSL_PRECOND( _rxForm.is(), "FormViewPageWindowAdapter::updateTabOrder: illegal argument!" );
355 if ( !_rxForm.is() )
356 return;
360 Reference< XTabController > xTabCtrl( getController( _rxForm ) );
361 if ( xTabCtrl.is() )
362 { // if there already is a TabController for this form, then delegate the "updateTabOrder" request
363 xTabCtrl->activateTabOrder();
365 else
366 { // otherwise, create a TabController
368 // if it's a sub form, then we must ensure there exist TabControllers
369 // for all its ancestors, too
370 Reference< XForm > xParentForm( _rxForm->getParent(), UNO_QUERY );
371 // there is a parent form -> look for the respective controller
372 Reference< XFormController > xParentController;
373 if ( xParentForm.is() )
374 xParentController = getController( xParentForm );
376 setController( _rxForm, xParentController );
379 catch (const Exception&)
381 DBG_UNHANDLED_EXCEPTION("svx");
386 FmXFormView::FmXFormView(FmFormView* _pView )
387 :m_pMarkedGrid(nullptr)
388 ,m_pView(_pView)
389 ,m_nActivationEvent(nullptr)
390 ,m_nErrorMessageEvent( nullptr )
391 ,m_nAutoFocusEvent( nullptr )
392 ,m_nControlWizardEvent( nullptr )
393 ,m_bFirstActivation( true )
394 ,m_isTabOrderUpdateSuspended( false )
399 void FmXFormView::cancelEvents()
401 if ( m_nActivationEvent )
403 Application::RemoveUserEvent( m_nActivationEvent );
404 m_nActivationEvent = nullptr;
407 if ( m_nErrorMessageEvent )
409 Application::RemoveUserEvent( m_nErrorMessageEvent );
410 m_nErrorMessageEvent = nullptr;
413 if ( m_nAutoFocusEvent )
415 Application::RemoveUserEvent( m_nAutoFocusEvent );
416 m_nAutoFocusEvent = nullptr;
419 if ( m_nControlWizardEvent )
421 Application::RemoveUserEvent( m_nControlWizardEvent );
422 m_nControlWizardEvent = nullptr;
427 void FmXFormView::notifyViewDying( )
429 DBG_ASSERT( m_pView, "FmXFormView::notifyViewDying: my view already died!" );
430 m_pView = nullptr;
431 cancelEvents();
435 FmXFormView::~FmXFormView()
437 DBG_ASSERT( m_aPageWindowAdapters.empty(), "FmXFormView::~FmXFormView: Window list not empty!" );
438 for (const auto& rpAdapter : m_aPageWindowAdapters)
440 rpAdapter->dispose();
443 cancelEvents();
446 // EventListener
448 void SAL_CALL FmXFormView::disposing(const EventObject& Source)
450 if ( m_xWindow.is() && Source.Source == m_xWindow )
452 m_xWindow->removeFocusListener(this);
453 if ( m_pView )
455 m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
457 m_xWindow = nullptr;
461 // XFormControllerListener
463 void SAL_CALL FmXFormView::formActivated(const EventObject& rEvent)
465 if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
466 m_pView->GetFormShell()->GetImpl()->formActivated( rEvent );
470 void SAL_CALL FmXFormView::formDeactivated(const EventObject& rEvent)
472 if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
473 m_pView->GetFormShell()->GetImpl()->formDeactivated( rEvent );
476 // XContainerListener
478 void SAL_CALL FmXFormView::elementInserted(const ContainerEvent& evt)
482 Reference< XControlContainer > xControlContainer( evt.Source, UNO_QUERY_THROW );
483 Reference< XControl > xControl( evt.Element, UNO_QUERY_THROW );
484 Reference< XFormComponent > xControlModel( xControl->getModel(), UNO_QUERY_THROW );
485 Reference< XForm > xForm( xControlModel->getParent(), UNO_QUERY_THROW );
487 if ( m_isTabOrderUpdateSuspended )
489 // remember the container and the control, so we can update the tab order on resumeTabOrderUpdate
490 m_aNeedTabOrderUpdate[ xControlContainer ].insert( xForm );
492 else
494 rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( xControlContainer );
495 if ( pAdapter.is() )
496 pAdapter->updateTabOrder( xForm );
499 catch (const Exception&)
501 DBG_UNHANDLED_EXCEPTION("svx");
506 void SAL_CALL FmXFormView::elementReplaced(const ContainerEvent& evt)
508 elementInserted(evt);
512 void SAL_CALL FmXFormView::elementRemoved(const ContainerEvent& /*evt*/)
517 rtl::Reference< FormViewPageWindowAdapter > FmXFormView::findWindow( const Reference< XControlContainer >& _rxCC ) const
519 auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
520 [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
521 if (i != m_aPageWindowAdapters.end())
522 return *i;
523 return nullptr;
527 void FmXFormView::addWindow(const SdrPageWindow& rWindow)
529 FmFormPage* pFormPage = dynamic_cast<FmFormPage*>( rWindow.GetPageView().GetPage() );
530 if ( !pFormPage )
531 return;
533 const Reference< XControlContainer >& xCC = rWindow.GetControlContainer();
534 if ( xCC.is()
535 && ( !findWindow( xCC ).is() )
538 rtl::Reference< FormViewPageWindowAdapter > pAdapter = new FormViewPageWindowAdapter( comphelper::getProcessComponentContext(), rWindow, this );
539 m_aPageWindowAdapters.push_back( pAdapter );
541 // listen at the ControlContainer to notice changes
542 Reference< XContainer > xContainer( xCC, UNO_QUERY );
543 if ( xContainer.is() )
544 xContainer->addContainerListener( this );
549 void FmXFormView::removeWindow( const Reference< XControlContainer >& _rxCC )
551 // Is called if
552 // - the design mode is being switched to
553 // - a window is deleted while in the design mode
554 // - the control container for a window is removed while the active mode is on
556 auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
557 [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
558 if (i != m_aPageWindowAdapters.end())
560 Reference< XContainer > xContainer( _rxCC, UNO_QUERY );
561 if ( xContainer.is() )
562 xContainer->removeContainerListener( this );
564 (*i)->dispose();
565 m_aPageWindowAdapters.erase( i );
569 void FmXFormView::displayAsyncErrorMessage( const SQLErrorEvent& _rEvent )
571 DBG_ASSERT( nullptr == m_nErrorMessageEvent, "FmXFormView::displayAsyncErrorMessage: not too fast, please!" );
572 // This should not happen - usually, the PostUserEvent is faster than any possible user
573 // interaction which could trigger a new error. If it happens, we need a queue for the events.
574 m_aAsyncError = _rEvent;
575 m_nErrorMessageEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnDelayedErrorMessage ) );
578 IMPL_LINK_NOARG(FmXFormView, OnDelayedErrorMessage, void*, void)
580 m_nErrorMessageEvent = nullptr;
581 displayException(m_aAsyncError, GetParentWindow());
584 void FmXFormView::onFirstViewActivation( const FmFormModel* _pDocModel )
586 if ( _pDocModel && _pDocModel->GetAutoControlFocus() )
587 m_nAutoFocusEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnAutoFocus ) );
590 void FmXFormView::suspendTabOrderUpdate()
592 OSL_ENSURE( !m_isTabOrderUpdateSuspended, "FmXFormView::suspendTabOrderUpdate: nesting not allowed!" );
593 m_isTabOrderUpdateSuspended = true;
596 void FmXFormView::resumeTabOrderUpdate()
598 OSL_ENSURE( m_isTabOrderUpdateSuspended, "FmXFormView::resumeTabOrderUpdate: not suspended!" );
599 m_isTabOrderUpdateSuspended = false;
601 // update the tab orders for all components which were collected since the suspendTabOrderUpdate call.
602 for (const auto& rContainer : m_aNeedTabOrderUpdate)
604 rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( rContainer.first );
605 if ( !pAdapter.is() )
606 continue;
608 for (const auto& rForm : rContainer.second)
610 pAdapter->updateTabOrder( rForm );
613 m_aNeedTabOrderUpdate.clear();
616 namespace
618 bool isActivableDatabaseForm(const Reference< XFormController > &xController)
620 // only database forms are to be activated
621 Reference< XRowSet > xForm(xController->getModel(), UNO_QUERY);
622 if ( !xForm.is() || !getConnection( xForm ).is() )
623 return false;
625 Reference< XPropertySet > xFormSet( xForm, UNO_QUERY );
626 if ( !xFormSet.is() )
628 SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form which does not have properties?" );
629 return false;
632 const OUString aSource = ::comphelper::getString( xFormSet->getPropertyValue( FM_PROP_COMMAND ) );
634 return !aSource.isEmpty();
637 class find_active_databaseform
639 const Reference< XFormController > xActiveController;
641 public:
643 explicit find_active_databaseform( const Reference< XFormController >& _xActiveController )
644 : xActiveController(_xActiveController )
647 Reference < XFormController > operator() (const Reference< XFormController > &xController)
649 if(xController == xActiveController && isActivableDatabaseForm(xController))
650 return xController;
652 if ( !xController.is() )
654 SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form controller which does not have children?" );
655 return nullptr;
658 for(sal_Int32 i = 0; i < xController->getCount(); ++i)
660 const Any a(xController->getByIndex(i));
661 Reference < XFormController > xI;
662 if ((a >>= xI) && xI.is())
664 Reference < XFormController > xRes(operator()(xI));
665 if (xRes.is())
666 return xRes;
670 return nullptr;
676 IMPL_LINK_NOARG(FmXFormView, OnActivate, void*, void)
678 m_nActivationEvent = nullptr;
680 if ( !m_pView )
682 OSL_FAIL( "FmXFormView::OnActivate: well... seems we have a timing problem (the view already died)!" );
683 return;
686 // setting the controller to activate
687 if (!(m_pView->GetFormShell() && m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW))
688 return;
690 FmXFormShell* const pShImpl = m_pView->GetFormShell()->GetImpl();
692 if(!pShImpl)
693 return;
695 find_active_databaseform fad(pShImpl->getActiveController_Lock());
697 vcl::Window* pWindow = m_pView->GetActualOutDev()->GetOwnerWindow();
698 rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
699 for (const auto& rpPageWindowAdapter : m_aPageWindowAdapters)
701 if ( pWindow == rpPageWindowAdapter->getWindow() )
702 pAdapter = rpPageWindowAdapter;
705 if ( !pAdapter.is() )
706 return;
708 Reference< XFormController > xControllerToActivate;
709 for (const Reference< XFormController > & xController : pAdapter->GetList())
711 if ( !xController.is() )
712 continue;
715 Reference< XFormController > xActiveController(fad(xController));
716 if (xActiveController.is())
718 xControllerToActivate = xActiveController;
719 break;
723 if(xControllerToActivate.is() || !isActivableDatabaseForm(xController))
724 continue;
726 xControllerToActivate = xController;
728 pShImpl->setActiveController_Lock(xControllerToActivate);
732 void FmXFormView::Activate(bool bSync)
734 if (m_nActivationEvent)
736 Application::RemoveUserEvent(m_nActivationEvent);
737 m_nActivationEvent = nullptr;
740 if (bSync)
742 LINK(this,FmXFormView,OnActivate).Call(nullptr);
744 else
745 m_nActivationEvent = Application::PostUserEvent(LINK(this,FmXFormView,OnActivate));
749 void FmXFormView::Deactivate(bool bDeactivateController)
751 if (m_nActivationEvent)
753 Application::RemoveUserEvent(m_nActivationEvent);
754 m_nActivationEvent = nullptr;
757 FmXFormShell* pShImpl = m_pView->GetFormShell() ? m_pView->GetFormShell()->GetImpl() : nullptr;
758 if (pShImpl && bDeactivateController)
759 pShImpl->setActiveController_Lock(nullptr);
763 FmFormShell* FmXFormView::GetFormShell() const
765 return m_pView ? m_pView->GetFormShell() : nullptr;
768 void FmXFormView::AutoFocus()
770 if (m_nAutoFocusEvent)
771 Application::RemoveUserEvent(m_nAutoFocusEvent);
773 m_nAutoFocusEvent = Application::PostUserEvent(LINK(this, FmXFormView, OnAutoFocus));
777 bool FmXFormView::isFocusable( const Reference< XControl >& i_rControl )
779 if ( !i_rControl.is() )
780 return false;
784 Reference< XPropertySet > xModelProps( i_rControl->getModel(), UNO_QUERY_THROW );
786 // only enabled controls are allowed to participate
787 bool bEnabled = false;
788 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_ENABLED ) >>= bEnabled );
789 if ( !bEnabled )
790 return false;
792 // check the class id of the control model
793 sal_Int16 nClassId = FormComponentType::CONTROL;
794 OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
796 // controls which are not focussable
797 if ( ( FormComponentType::CONTROL != nClassId )
798 && ( FormComponentType::IMAGEBUTTON != nClassId )
799 && ( FormComponentType::GROUPBOX != nClassId )
800 && ( FormComponentType::FIXEDTEXT != nClassId )
801 && ( FormComponentType::HIDDENCONTROL != nClassId )
802 && ( FormComponentType::IMAGECONTROL != nClassId )
803 && ( FormComponentType::SCROLLBAR != nClassId )
804 && ( FormComponentType::SPINBUTTON!= nClassId )
807 return true;
810 catch (const Exception&)
812 DBG_UNHANDLED_EXCEPTION("svx");
814 return false;
818 static Reference< XControl > lcl_firstFocussableControl( const Sequence< Reference< XControl > >& _rControls )
820 Reference< XControl > xReturn;
822 // loop through all the controls
823 for ( auto const & control : _rControls )
825 if ( !control.is() )
826 continue;
828 if ( FmXFormView::isFocusable( control ) )
830 xReturn = control;
831 break;
835 if ( !xReturn.is() && _rControls.hasElements() )
836 xReturn = _rControls[0];
838 return xReturn;
842 namespace
845 void lcl_ensureControlsOfFormExist_nothrow( const SdrPage& _rPage, const SdrView& _rView, const vcl::Window& _rWindow, const Reference< XForm >& _rxForm )
849 Reference< XInterface > xNormalizedForm( _rxForm, UNO_QUERY_THROW );
851 SdrObjListIter aSdrObjectLoop( &_rPage, SdrIterMode::DeepNoGroups );
852 while ( aSdrObjectLoop.IsMore() )
854 FmFormObj* pFormObject = FmFormObj::GetFormObject( aSdrObjectLoop.Next() );
855 if ( !pFormObject )
856 continue;
858 Reference< XChild > xModel( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
859 Reference< XInterface > xModelParent( xModel->getParent(), UNO_QUERY );
861 if ( xNormalizedForm.get() != xModelParent.get() )
862 continue;
864 pFormObject->GetUnoControl( _rView, *_rWindow.GetOutDev() );
867 catch (const Exception&)
869 DBG_UNHANDLED_EXCEPTION("svx");
875 Reference< XFormController > FmXFormView::getFormController( const Reference< XForm >& _rxForm, const OutputDevice& _rDevice ) const
877 Reference< XFormController > xController;
879 for (const rtl::Reference< FormViewPageWindowAdapter >& pAdapter : m_aPageWindowAdapters)
881 if ( !pAdapter )
883 SAL_WARN( "svx.form", "FmXFormView::getFormController: invalid page window adapter!" );
884 continue;
887 if ( pAdapter->getWindow() != _rDevice.GetOwnerWindow() )
888 // wrong device
889 continue;
891 xController = pAdapter->getController( _rxForm );
892 if ( xController.is() )
893 break;
895 return xController;
899 IMPL_LINK_NOARG(FmXFormView, OnAutoFocus, void*, void)
901 m_nAutoFocusEvent = nullptr;
903 // go to the first form of our page, examine it's TabController, go to its first (in terms of the tab order)
904 // control, give it the focus
906 SdrPageView *pPageView = m_pView ? m_pView->GetSdrPageView() : nullptr;
907 SdrPage *pSdrPage = pPageView ? pPageView->GetPage() : nullptr;
908 // get the forms collection of the page we belong to
909 FmFormPage* pPage = dynamic_cast<FmFormPage*>( pSdrPage );
910 Reference< XIndexAccess > xForms( pPage ? Reference< XIndexAccess >( pPage->GetForms() ) : Reference< XIndexAccess >() );
912 const rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
913 const vcl::Window* pWindow = pAdapter ? pAdapter->getWindow() : nullptr;
915 ENSURE_OR_RETURN_VOID( xForms.is() && pWindow, "FmXFormView::OnAutoFocus: could not collect all essentials!" );
919 // go for the tab controller of the first form
920 if ( !xForms->getCount() )
921 return;
922 Reference< XForm > xForm( xForms->getByIndex( 0 ), UNO_QUERY_THROW );
923 Reference< XTabController > xTabController( pAdapter->getController( xForm ), UNO_QUERY_THROW );
925 // go for the first control of the controller
926 Sequence< Reference< XControl > > aControls( xTabController->getControls() );
927 if ( !aControls.hasElements() )
929 Reference< XElementAccess > xFormElementAccess( xForm, UNO_QUERY_THROW );
930 if (xFormElementAccess->hasElements() && pPage && m_pView)
932 // there are control models in the form, but no controls, yet.
933 // Well, since some time controls are created on demand only. In particular,
934 // they're normally created when they're first painted.
935 // Unfortunately, the FormController does not have any way to
936 // trigger the creation itself, so we must hack this ...
937 lcl_ensureControlsOfFormExist_nothrow( *pPage, *m_pView, *pWindow, xForm );
938 aControls = xTabController->getControls();
939 OSL_ENSURE( aControls.hasElements(), "FmXFormView::OnAutoFocus: no controls at all!" );
943 // set the focus to this first control
944 Reference< XWindow > xControlWindow( lcl_firstFocussableControl( aControls ), UNO_QUERY );
945 if ( !xControlWindow.is() )
946 return;
948 xControlWindow->setFocus();
950 // ensure that the control is visible
951 // 80210 - 12/07/00 - FS
952 const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
953 const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
954 if ( pCurrentWindow )
956 awt::Rectangle aRect = xControlWindow->getPosSize();
957 ::tools::Rectangle aNonUnoRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
958 m_pView->MakeVisible( pCurrentWindow->PixelToLogic( aNonUnoRect ), *const_cast< vcl::Window* >( pCurrentWindow ) );
961 catch (const Exception&)
963 DBG_UNHANDLED_EXCEPTION("svx");
968 void FmXFormView::onCreatedFormObject( FmFormObj const & _rFormObject )
970 FmFormShell* pShell = m_pView ? m_pView->GetFormShell() : nullptr;
971 FmXFormShell* pShellImpl = pShell ? pShell->GetImpl() : nullptr;
972 OSL_ENSURE( pShellImpl, "FmXFormView::onCreatedFormObject: no form shell!" );
973 if ( !pShellImpl )
974 return;
976 // it is valid that the form shell's forms collection is not initialized, yet
977 pShellImpl->UpdateForms_Lock(true);
979 m_xLastCreatedControlModel.set( _rFormObject.GetUnoControlModel(), UNO_QUERY );
980 if ( !m_xLastCreatedControlModel.is() )
981 return;
983 // some initial property defaults
984 FormControlFactory aControlFactory;
985 aControlFactory.initializeControlModel(pShellImpl->getDocumentType_Lock(), _rFormObject);
987 if (!pShellImpl->GetWizardUsing_Lock())
988 return;
990 // #i31958# don't call wizards in XForms mode
991 if (pShellImpl->isEnhancedForm_Lock())
992 return;
994 // #i46898# no wizards if there is no Base installed - currently, all wizards are
995 // database related
996 if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
997 return;
999 if ( m_nControlWizardEvent )
1000 Application::RemoveUserEvent( m_nControlWizardEvent );
1001 m_nControlWizardEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnStartControlWizard ) );
1004 void FmXFormView::breakCreateFormObject()
1006 if (m_nControlWizardEvent != nullptr)
1008 Application::RemoveUserEvent(m_nControlWizardEvent);
1009 m_nControlWizardEvent = nullptr;
1011 m_xLastCreatedControlModel.clear();
1014 Reference<XWindow> FmXFormView::GetParentWindow() const
1016 const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
1017 const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
1018 return VCLUnoHelper::GetInterface(const_cast<vcl::Window*>(pCurrentWindow));
1021 IMPL_LINK_NOARG( FmXFormView, OnStartControlWizard, void*, void )
1023 m_nControlWizardEvent = nullptr;
1024 OSL_PRECOND( m_xLastCreatedControlModel.is(), "FmXFormView::OnStartControlWizard: illegal call!" );
1025 if ( !m_xLastCreatedControlModel.is() )
1026 return;
1028 sal_Int16 nClassId = FormComponentType::CONTROL;
1031 OSL_VERIFY( m_xLastCreatedControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
1033 catch (const Exception&)
1035 DBG_UNHANDLED_EXCEPTION("svx");
1038 const char* pWizardAsciiName = nullptr;
1039 switch ( nClassId )
1041 case FormComponentType::GRIDCONTROL:
1042 pWizardAsciiName = "com.sun.star.sdb.GridControlAutoPilot";
1043 break;
1044 case FormComponentType::LISTBOX:
1045 case FormComponentType::COMBOBOX:
1046 pWizardAsciiName = "com.sun.star.sdb.ListComboBoxAutoPilot";
1047 break;
1048 case FormComponentType::GROUPBOX:
1049 pWizardAsciiName = "com.sun.star.sdb.GroupBoxAutoPilot";
1050 break;
1053 if ( pWizardAsciiName )
1055 // build the argument list
1056 ::comphelper::NamedValueCollection aWizardArgs;
1057 aWizardArgs.put("ObjectModel", m_xLastCreatedControlModel);
1058 aWizardArgs.put("ParentWindow", GetParentWindow());
1060 // create the wizard object
1061 Reference< XExecutableDialog > xWizard;
1064 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1065 xWizard.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii(pWizardAsciiName), aWizardArgs.getWrappedPropertyValues(), xContext ), UNO_QUERY);
1067 catch (const Exception&)
1069 DBG_UNHANDLED_EXCEPTION("svx");
1072 if ( !xWizard.is() )
1074 ShowServiceNotAvailableError( nullptr, OUString::createFromAscii(pWizardAsciiName), true );
1076 else
1078 // execute the wizard
1081 xWizard->execute();
1083 catch (const Exception&)
1085 DBG_UNHANDLED_EXCEPTION("svx");
1090 m_xLastCreatedControlModel.clear();
1094 namespace
1096 void lcl_insertIntoFormComponentHierarchy_throw( const FmFormView& _rView, const SdrUnoObj& _rSdrObj,
1097 const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
1098 const OUString& _rCommand, const sal_Int32 _nCommandType )
1100 FmFormPage& rPage = static_cast< FmFormPage& >( *_rView.GetSdrPageView()->GetPage() );
1102 Reference< XFormComponent > xFormComponent( _rSdrObj.GetUnoControlModel(), UNO_QUERY_THROW );
1103 Reference< XForm > xTargetForm(
1104 rPage.GetImpl().findPlaceInFormComponentHierarchy( xFormComponent, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType ),
1105 UNO_SET_THROW );
1107 FmFormPageImpl::setUniqueName( xFormComponent, xTargetForm );
1109 Reference< XIndexContainer > xFormAsContainer( xTargetForm, UNO_QUERY_THROW );
1110 xFormAsContainer->insertByIndex( xFormAsContainer->getCount(), makeAny( xFormComponent ) );
1115 SdrObjectUniquePtr FmXFormView::implCreateFieldControl( const svx::ODataAccessDescriptor& _rColumnDescriptor )
1117 // not if we're in design mode
1118 if ( !m_pView->IsDesignMode() )
1119 return nullptr;
1121 OUString sCommand, sFieldName;
1122 sal_Int32 nCommandType = CommandType::COMMAND;
1123 SharedConnection xConnection;
1125 OUString sDataSource = _rColumnDescriptor.getDataSource();
1126 _rColumnDescriptor[ DataAccessDescriptorProperty::Command ] >>= sCommand;
1127 _rColumnDescriptor[ DataAccessDescriptorProperty::ColumnName ] >>= sFieldName;
1128 _rColumnDescriptor[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType;
1130 Reference< XConnection > xExternalConnection;
1131 _rColumnDescriptor[ DataAccessDescriptorProperty::Connection ] >>= xExternalConnection;
1132 xConnection.reset( xExternalConnection, SharedConnection::NoTakeOwnership );
1135 if ( sCommand.isEmpty()
1136 || sFieldName.isEmpty()
1137 || ( sDataSource.isEmpty()
1138 && !xConnection.is()
1142 OSL_FAIL( "FmXFormView::implCreateFieldControl: nonsense!" );
1145 Reference< XDataSource > xDataSource;
1146 SQLErrorEvent aError;
1149 if ( xConnection.is() && !xDataSource.is() && sDataSource.isEmpty() )
1151 Reference< XChild > xChild( xConnection, UNO_QUERY );
1152 if ( xChild.is() )
1153 xDataSource.set(xChild->getParent(), css::uno::UNO_QUERY);
1156 // obtain the data source
1157 if ( !xDataSource.is() )
1158 xDataSource = getDataSource( sDataSource, comphelper::getProcessComponentContext() );
1160 // and the connection, if necessary
1161 if ( !xConnection.is() )
1162 xConnection.reset( getConnection_withFeedback(
1163 sDataSource,
1164 OUString(),
1165 OUString(),
1166 comphelper::getProcessComponentContext(),
1167 nullptr
1168 ) );
1170 catch (const SQLException&)
1172 aError.Reason = ::cppu::getCaughtException();
1174 catch (const Exception& )
1176 /* will be asserted below */
1178 if (aError.Reason.hasValue())
1180 displayAsyncErrorMessage( aError );
1181 return nullptr;
1184 // need a data source and a connection here
1185 if (!xDataSource.is() || !xConnection.is())
1187 OSL_FAIL("FmXFormView::implCreateFieldControl : could not retrieve the data source or the connection!");
1188 return nullptr;
1191 Reference< XComponent > xKeepFieldsAlive;
1192 // go
1195 // determine the table/query field which we should create a control for
1196 Reference< XPropertySet > xField;
1198 Reference< XNameAccess > xFields = getFieldsByCommandDescriptor(
1199 xConnection, nCommandType, sCommand, xKeepFieldsAlive );
1201 if (xFields.is() && xFields->hasByName(sFieldName))
1202 xFields->getByName(sFieldName) >>= xField;
1203 if ( !xField.is() )
1204 return nullptr;
1206 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( xConnection ), UNO_SET_THROW );
1207 Reference< XNumberFormats > xNumberFormats( xSupplier->getNumberFormats(), UNO_SET_THROW );
1209 OUString sLabelPostfix;
1212 // only for text size
1213 OutputDevice* pOutDev = nullptr;
1214 if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
1215 pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
1216 else
1217 {// find OutDev
1218 if (SdrPageView* pPageView = m_pView->GetSdrPageView())
1220 // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
1221 // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();
1223 for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
1225 const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
1227 if( rPageWindow.GetPaintWindow().OutputToWindow())
1229 pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
1230 break;
1236 if ( !pOutDev )
1237 return nullptr;
1239 sal_Int32 nDataType = ::comphelper::getINT32(xField->getPropertyValue(FM_PROP_FIELDTYPE));
1240 if ((DataType::BINARY == nDataType) || (DataType::VARBINARY == nDataType))
1241 return nullptr;
1244 // determine the control type by examining the data type of the bound column
1245 SdrObjKind nOBJID = OBJ_NONE;
1246 bool bDateNTimeField = false;
1248 bool bIsCurrency = false;
1249 if (::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField))
1250 bIsCurrency = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY));
1252 if (bIsCurrency)
1253 nOBJID = OBJ_FM_CURRENCYFIELD;
1254 else
1255 switch (nDataType)
1257 case DataType::BLOB:
1258 case DataType::LONGVARBINARY:
1259 nOBJID = OBJ_FM_IMAGECONTROL;
1260 break;
1261 case DataType::LONGVARCHAR:
1262 case DataType::CLOB:
1263 nOBJID = OBJ_FM_EDIT;
1264 break;
1265 case DataType::BINARY:
1266 case DataType::VARBINARY:
1267 return nullptr;
1268 case DataType::BIT:
1269 case DataType::BOOLEAN:
1270 nOBJID = OBJ_FM_CHECKBOX;
1271 break;
1272 case DataType::TINYINT:
1273 case DataType::SMALLINT:
1274 case DataType::INTEGER:
1275 nOBJID = OBJ_FM_NUMERICFIELD;
1276 break;
1277 case DataType::REAL:
1278 case DataType::DOUBLE:
1279 case DataType::NUMERIC:
1280 case DataType::DECIMAL:
1281 nOBJID = OBJ_FM_FORMATTEDFIELD;
1282 break;
1283 case DataType::TIMESTAMP:
1284 bDateNTimeField = true;
1285 sLabelPostfix = SvxResId(RID_STR_POSTFIX_DATE);
1286 [[fallthrough]];
1287 case DataType::DATE:
1288 nOBJID = OBJ_FM_DATEFIELD;
1289 break;
1290 case DataType::TIME:
1291 nOBJID = OBJ_FM_TIMEFIELD;
1292 break;
1293 case DataType::CHAR:
1294 case DataType::VARCHAR:
1295 default:
1296 nOBJID = OBJ_FM_EDIT;
1297 break;
1299 if (!nOBJID)
1300 return nullptr;
1302 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pLabel;
1303 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pControl;
1304 if ( !createControlLabelPair( *pOutDev, 0, 0, xField, xNumberFormats, nOBJID, sLabelPostfix,
1305 pLabel, pControl, xDataSource, sDataSource, sCommand, nCommandType )
1308 return nullptr;
1312 // group objects
1313 bool bCheckbox = ( OBJ_FM_CHECKBOX == nOBJID );
1314 OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateFieldControl: why was there a label created for a check box?" );
1315 if ( bCheckbox )
1316 return SdrObjectUniquePtr(pControl.release());
1318 SdrObjGroup* pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView());
1319 SdrObjList* pObjList = pGroup->GetSubList();
1320 pObjList->InsertObject( pLabel.release() );
1321 pObjList->InsertObject( pControl.release() );
1323 if ( bDateNTimeField )
1324 { // so far we created a date field only, but we also need a time field
1325 if ( createControlLabelPair( *pOutDev, 0, 1000, xField, xNumberFormats, OBJ_FM_TIMEFIELD,
1326 SvxResId(RID_STR_POSTFIX_TIME), pLabel, pControl,
1327 xDataSource, sDataSource, sCommand, nCommandType )
1330 pObjList->InsertObject( pLabel.release() );
1331 pObjList->InsertObject( pControl.release() );
1335 return SdrObjectUniquePtr(pGroup); // and done
1337 catch (const Exception&)
1339 DBG_UNHANDLED_EXCEPTION("svx");
1343 return nullptr;
1347 SdrObjectUniquePtr FmXFormView::implCreateXFormsControl( const svx::OXFormsDescriptor &_rDesc )
1349 // not if we're in design mode
1350 if ( !m_pView->IsDesignMode() )
1351 return nullptr;
1353 // go
1356 // determine the table/query field which we should create a control for
1357 Reference< XNumberFormats > xNumberFormats;
1358 OUString sLabelPostfix = _rDesc.szName;
1361 // only for text size
1362 OutputDevice* pOutDev = nullptr;
1363 if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
1364 pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
1365 else
1366 {// find OutDev
1367 if (SdrPageView* pPageView = m_pView->GetSdrPageView())
1369 // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
1370 // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();
1372 for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
1374 const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
1376 if( rPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType() == OUTDEV_WINDOW)
1378 pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
1379 break;
1385 if ( !pOutDev )
1386 return nullptr;
1389 // The service name decides which control should be created
1390 SdrObjKind nOBJID = OBJ_FM_EDIT;
1391 if(_rDesc.szServiceName == FM_SUN_COMPONENT_NUMERICFIELD)
1392 nOBJID = OBJ_FM_NUMERICFIELD;
1393 if(_rDesc.szServiceName == FM_SUN_COMPONENT_CHECKBOX)
1394 nOBJID = OBJ_FM_CHECKBOX;
1395 if(_rDesc.szServiceName == FM_COMPONENT_COMMANDBUTTON)
1396 nOBJID = OBJ_FM_BUTTON;
1398 Reference< css::form::submission::XSubmission > xSubmission(_rDesc.xPropSet, UNO_QUERY);
1400 // xform control or submission button?
1401 if ( !xSubmission.is() )
1403 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pLabel;
1404 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pControl;
1405 if ( !createControlLabelPair( *pOutDev, 0, 0, nullptr, xNumberFormats, nOBJID, sLabelPostfix,
1406 pLabel, pControl, nullptr, "", "", -1 )
1409 return nullptr;
1413 // Now build the connection between the control and the data item.
1414 Reference< XValueBinding > xValueBinding(_rDesc.xPropSet,UNO_QUERY);
1415 Reference< XBindableValue > xBindableValue(pControl->GetUnoControlModel(),UNO_QUERY);
1417 DBG_ASSERT( xBindableValue.is(), "FmXFormView::implCreateXFormsControl: control's not bindable!" );
1418 if ( xBindableValue.is() )
1419 xBindableValue->setValueBinding(xValueBinding);
1421 bool bCheckbox = ( OBJ_FM_CHECKBOX == nOBJID );
1422 OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateXFormsControl: why was there a label created for a check box?" );
1423 if ( bCheckbox )
1424 return SdrObjectUniquePtr(pControl.release());
1427 // group objects
1428 SdrObjGroup* pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView());
1429 SdrObjList* pObjList = pGroup->GetSubList();
1430 pObjList->InsertObject(pLabel.release());
1431 pObjList->InsertObject(pControl.release());
1433 return SdrObjectUniquePtr(pGroup);
1435 else {
1437 // create a button control
1438 const MapMode& eTargetMode( pOutDev->GetMapMode() );
1439 const MapMode eSourceMode(MapUnit::Map100thMM);
1440 const SdrObjKind nObjID = OBJ_FM_BUTTON;
1441 ::Size controlSize(4000, 500);
1442 FmFormObj *pControl = static_cast<FmFormObj*>(
1443 SdrObjFactory::MakeNewObject(
1444 getView()->getSdrModelFromSdrView(),
1445 SdrInventor::FmForm,
1446 nObjID));
1447 controlSize.setWidth( tools::Long(controlSize.Width() * eTargetMode.GetScaleX()) );
1448 controlSize.setHeight( tools::Long(controlSize.Height() * eTargetMode.GetScaleY()) );
1449 ::Point controlPos( OutputDevice::LogicToLogic( ::Point( controlSize.Width(), 0 ), eSourceMode, eTargetMode ) );
1450 ::tools::Rectangle controlRect( controlPos, OutputDevice::LogicToLogic( controlSize, eSourceMode, eTargetMode ) );
1451 pControl->SetLogicRect(controlRect);
1453 // set the button label
1454 Reference< XPropertySet > xControlSet(pControl->GetUnoControlModel(), UNO_QUERY);
1455 xControlSet->setPropertyValue(FM_PROP_LABEL, makeAny(_rDesc.szName));
1457 // connect the submission with the submission supplier (aka the button)
1458 xControlSet->setPropertyValue( FM_PROP_BUTTON_TYPE,
1459 makeAny( FormButtonType_SUBMIT ) );
1460 Reference< css::form::submission::XSubmissionSupplier > xSubmissionSupplier(pControl->GetUnoControlModel(), UNO_QUERY);
1461 xSubmissionSupplier->setSubmission(xSubmission);
1463 return SdrObjectUniquePtr(pControl);
1466 catch (const Exception&)
1468 TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while creating the control !");
1472 return nullptr;
1475 bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
1476 const Reference< XPropertySet >& _rxField, const Reference< XNumberFormats >& _rxNumberFormats,
1477 SdrObjKind _nControlObjectID, std::u16string_view _rFieldPostfix,
1478 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel,
1479 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl,
1480 const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
1481 const OUString& _rCommand, const sal_Int32 _nCommandType )
1483 if(!createControlLabelPair(
1484 _rOutDev,
1485 _nXOffsetMM,
1486 _nYOffsetMM,
1487 _rxField,
1488 _rxNumberFormats,
1489 _nControlObjectID,
1490 _rFieldPostfix,
1491 SdrInventor::FmForm,
1492 OBJ_FM_FIXEDTEXT,
1494 // tdf#118963 Hand over a SdrModel to SdrObject-creation. It uses the local m_pView
1495 // and already returning false when nullptr == getView() could be done, but m_pView
1496 // is already dereferenced here in many places (see below), so just use it for now.
1497 getView()->getSdrModelFromSdrView(),
1499 _rpLabel,
1500 _rpControl))
1502 return false;
1505 // insert the control model(s) into the form component hierarchy
1506 if ( _rpLabel )
1507 lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpLabel, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
1508 lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpControl, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
1510 // some context-dependent initializations
1511 FormControlFactory aControlFactory;
1512 if ( _rpLabel )
1513 aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpLabel );
1514 aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpControl );
1516 return true;
1520 bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
1521 const Reference< XPropertySet >& _rxField,
1522 const Reference< XNumberFormats >& _rxNumberFormats, SdrObjKind _nControlObjectID,
1523 std::u16string_view _rFieldPostfix, SdrInventor _nInventor, SdrObjKind _nLabelObjectID,
1524 SdrModel& _rModel,
1525 std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel, std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl)
1527 sal_Int32 nDataType = 0;
1528 OUString sFieldName;
1529 Any aFieldName;
1530 if ( _rxField.is() )
1532 nDataType = ::comphelper::getINT32(_rxField->getPropertyValue(FM_PROP_FIELDTYPE));
1533 aFieldName = _rxField->getPropertyValue(FM_PROP_NAME);
1534 aFieldName >>= sFieldName;
1537 // calculate the positions, respecting the settings of the target device
1538 ::Size aTextSize( _rOutDev.GetTextWidth(sFieldName + _rFieldPostfix), _rOutDev.GetTextHeight() );
1540 MapMode eTargetMode( _rOutDev.GetMapMode() ),
1541 eSourceMode( MapUnit::Map100thMM );
1543 // text width is at least 4 centimeters
1544 // text height is always half a centimeter
1545 ::Size aDefTxtSize(4000, 500);
1546 ::Size aDefSize(4000, 500);
1547 ::Size aDefImageSize(4000, 4000);
1549 ::Size aRealSize = OutputDevice::LogicToLogic(aTextSize, eTargetMode, eSourceMode);
1550 aRealSize.setWidth( std::max(aRealSize.Width(), aDefTxtSize.Width()) );
1551 aRealSize.setHeight( aDefSize.Height() );
1553 // adjust to scaling of the target device (#53523#)
1554 aRealSize.setWidth( tools::Long(Fraction(aRealSize.Width(), 1) * eTargetMode.GetScaleX()) );
1555 aRealSize.setHeight( tools::Long(Fraction(aRealSize.Height(), 1) * eTargetMode.GetScaleY()) );
1557 // for boolean fields, we do not create a label, but just a checkbox
1558 bool bNeedLabel = ( _nControlObjectID != OBJ_FM_CHECKBOX );
1560 // the label
1561 ::std::unique_ptr< SdrUnoObj, SdrObjectFreeOp > pLabel;
1562 Reference< XPropertySet > xLabelModel;
1564 if ( bNeedLabel )
1566 pLabel.reset( dynamic_cast< SdrUnoObj* >(
1567 SdrObjFactory::MakeNewObject(
1568 _rModel,
1569 _nInventor,
1570 _nLabelObjectID)));
1572 OSL_ENSURE(pLabel, "FmXFormView::createControlLabelPair: could not create the label!");
1574 if (!pLabel)
1575 return false;
1577 xLabelModel.set( pLabel->GetUnoControlModel(), UNO_QUERY );
1578 if ( xLabelModel.is() )
1580 OUString sLabel;
1581 if ( _rxField.is() && _rxField->getPropertySetInfo()->hasPropertyByName(FM_PROP_LABEL) )
1582 _rxField->getPropertyValue(FM_PROP_LABEL) >>= sLabel;
1583 if ( sLabel.isEmpty() )
1584 sLabel = sFieldName;
1586 xLabelModel->setPropertyValue( FM_PROP_LABEL, makeAny( sLabel + _rFieldPostfix ) );
1587 OUString sObjectLabel(SvxResId(RID_STR_OBJECT_LABEL).replaceAll("#object#", sFieldName));
1588 xLabelModel->setPropertyValue(FM_PROP_NAME, makeAny(sObjectLabel));
1591 pLabel->SetLogicRect( ::tools::Rectangle(
1592 OutputDevice::LogicToLogic( ::Point( _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
1593 OutputDevice::LogicToLogic( aRealSize, eSourceMode, eTargetMode )
1594 ) );
1597 // the control
1598 ::std::unique_ptr< SdrUnoObj, SdrObjectFreeOp > pControl( dynamic_cast< SdrUnoObj* >(
1599 SdrObjFactory::MakeNewObject(
1600 _rModel,
1601 _nInventor,
1602 _nControlObjectID)));
1604 OSL_ENSURE(pControl, "FmXFormView::createControlLabelPair: could not create the control!");
1606 if (!pControl)
1607 return false;
1609 Reference< XPropertySet > xControlSet( pControl->GetUnoControlModel(), UNO_QUERY );
1610 if ( !xControlSet.is() )
1611 return false;
1613 // size of the control
1614 ::Size aControlSize( aDefSize );
1615 switch ( nDataType )
1617 case DataType::BIT:
1618 case DataType::BOOLEAN:
1619 aControlSize = aDefSize;
1620 break;
1621 case DataType::LONGVARCHAR:
1622 case DataType::CLOB:
1623 case DataType::LONGVARBINARY:
1624 case DataType::BLOB:
1625 aControlSize = aDefImageSize;
1626 break;
1629 if ( OBJ_FM_IMAGECONTROL == _nControlObjectID )
1630 aControlSize = aDefImageSize;
1632 aControlSize.setWidth( tools::Long(Fraction(aControlSize.Width(), 1) * eTargetMode.GetScaleX()) );
1633 aControlSize.setHeight( tools::Long(Fraction(aControlSize.Height(), 1) * eTargetMode.GetScaleY()) );
1635 pControl->SetLogicRect( ::tools::Rectangle(
1636 OutputDevice::LogicToLogic( ::Point( aRealSize.Width() + _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
1637 OutputDevice::LogicToLogic( aControlSize, eSourceMode, eTargetMode )
1638 ) );
1640 // some initializations
1641 Reference< XPropertySetInfo > xControlPropInfo = xControlSet->getPropertySetInfo();
1643 if ( aFieldName.hasValue() )
1645 xControlSet->setPropertyValue( FM_PROP_CONTROLSOURCE, aFieldName );
1646 xControlSet->setPropertyValue( FM_PROP_NAME, aFieldName );
1647 if ( !bNeedLabel )
1649 // no dedicated label control => use the label property
1650 if ( xControlPropInfo->hasPropertyByName( FM_PROP_LABEL ) )
1651 xControlSet->setPropertyValue( FM_PROP_LABEL, makeAny( sFieldName + _rFieldPostfix ) );
1652 else
1653 OSL_FAIL( "FmXFormView::createControlLabelPair: can't set a label for the control!" );
1657 if ( (nDataType == DataType::LONGVARCHAR || nDataType == DataType::CLOB) && xControlPropInfo->hasPropertyByName( FM_PROP_MULTILINE ) )
1659 xControlSet->setPropertyValue( FM_PROP_MULTILINE, makeAny( true ) );
1662 // announce the label to the control
1663 if ( xControlPropInfo->hasPropertyByName( FM_PROP_CONTROLLABEL ) && xLabelModel.is() )
1667 xControlSet->setPropertyValue( FM_PROP_CONTROLLABEL, makeAny( xLabelModel ) );
1669 catch (const Exception&)
1671 DBG_UNHANDLED_EXCEPTION("svx");
1675 if ( _rxField.is() )
1677 FormControlFactory::initializeFieldDependentProperties( _rxField, xControlSet, _rxNumberFormats );
1680 _rpLabel = std::move(pLabel);
1681 _rpControl = std::move(pControl);
1682 return true;
1686 FmXFormView::ObjectRemoveListener::ObjectRemoveListener( FmXFormView* pParent )
1687 :m_pParent( pParent )
1692 void FmXFormView::ObjectRemoveListener::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1694 if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
1695 return;
1696 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
1697 if (pSdrHint->GetKind() == SdrHintKind::ObjectRemoved)
1698 m_pParent->ObjectRemovedInAliveMode(pSdrHint->GetObject());
1702 void FmXFormView::ObjectRemovedInAliveMode( const SdrObject* pObject )
1704 // if the remote object in my MarkList, which I have memorized when switching to the
1705 // Alive mode, I have to take it out now, because I otherwise try to set the mark
1706 // again when switching back (interestingly, this fails only with grouped objects
1707 // (when accessing their ObjList GPF), not with individual ones)
1709 const size_t nCount = m_aMark.GetMarkCount();
1710 for (size_t i = 0; i < nCount; ++i)
1712 SdrMark* pMark = m_aMark.GetMark(i);
1713 SdrObject* pCurrent = pMark->GetMarkedSdrObj();
1714 if (pObject == pCurrent)
1716 m_aMark.DeleteMark(i);
1717 return;
1719 // I do not need to descend into GroupObjects: if an object is deleted there,
1720 // then the pointer, which I have, to the GroupObject still remains valid ...
1725 void FmXFormView::stopMarkListWatching()
1727 if ( m_pWatchStoredList )
1729 m_pWatchStoredList->EndListeningAll();
1730 m_pWatchStoredList.reset();
1735 void FmXFormView::startMarkListWatching()
1737 if ( !m_pWatchStoredList )
1739 FmFormModel* pModel = GetFormShell() ? GetFormShell()->GetFormModel() : nullptr;
1740 DBG_ASSERT( pModel != nullptr, "FmXFormView::startMarkListWatching: shell has no model!" );
1741 if (pModel)
1743 m_pWatchStoredList.reset(new ObjectRemoveListener( this ));
1744 m_pWatchStoredList->StartListening( *static_cast< SfxBroadcaster* >( pModel ) );
1747 else
1749 OSL_FAIL( "FmXFormView::startMarkListWatching: already listening!" );
1753 void FmXFormView::saveMarkList()
1755 if ( m_pView )
1757 m_aMark = m_pView->GetMarkedObjectList();
1758 const size_t nCount = m_aMark.GetMarkCount( );
1759 for ( size_t i = 0; i < nCount; ++i )
1761 SdrMark* pMark = m_aMark.GetMark(i);
1762 SdrObject* pObj = pMark->GetMarkedSdrObj();
1764 if ( m_pView->IsObjMarked( pObj ) )
1766 if ( pObj->IsGroupObject() )
1768 SdrObjListIter aIter( pObj->GetSubList() );
1769 bool bMixed = false;
1770 while ( aIter.IsMore() && !bMixed )
1771 bMixed = ( aIter.Next()->GetObjInventor() != SdrInventor::FmForm );
1773 if ( !bMixed )
1775 // all objects in the group are form objects
1776 m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
1779 else
1781 if ( pObj->GetObjInventor() == SdrInventor::FmForm )
1782 { // this is a form layer object
1783 m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
1789 else
1791 OSL_FAIL( "FmXFormView::saveMarkList: invalid view!" );
1792 m_aMark.Clear();
1796 static bool lcl_hasObject( SdrObjListIter& rIter, SdrObject const * pObj )
1798 bool bFound = false;
1799 while (rIter.IsMore() && !bFound)
1800 bFound = pObj == rIter.Next();
1802 rIter.Reset();
1803 return bFound;
1807 void FmXFormView::restoreMarkList( SdrMarkList& _rRestoredMarkList )
1809 if ( !m_pView )
1810 return;
1812 _rRestoredMarkList.Clear();
1814 const SdrMarkList& rCurrentList = m_pView->GetMarkedObjectList();
1815 FmFormPage* pPage = GetFormShell() ? GetFormShell()->GetCurPage() : nullptr;
1816 if (!pPage)
1817 return;
1819 if (rCurrentList.GetMarkCount())
1820 { // there is a current mark ... hmm. Is it a subset of the mark we remembered in saveMarkList?
1821 bool bMisMatch = false;
1823 // loop through all current marks
1824 const size_t nCurrentCount = rCurrentList.GetMarkCount();
1825 for ( size_t i=0; i<nCurrentCount && !bMisMatch; ++i )
1827 const SdrObject* pCurrentMarked = rCurrentList.GetMark( i )->GetMarkedSdrObj();
1829 // loop through all saved marks, check for equality
1830 bool bFound = false;
1831 const size_t nSavedCount = m_aMark.GetMarkCount();
1832 for ( size_t j=0; j<nSavedCount && !bFound; ++j )
1834 if ( m_aMark.GetMark( j )->GetMarkedSdrObj() == pCurrentMarked )
1835 bFound = true;
1838 // did not find a current mark in the saved marks
1839 if ( !bFound )
1840 bMisMatch = true;
1843 if ( bMisMatch )
1845 m_aMark.Clear();
1846 _rRestoredMarkList = rCurrentList;
1847 return;
1850 // it is important that the objects of the mark list are not accessed,
1851 // because they can be already destroyed
1852 SdrPageView* pCurPageView = m_pView->GetSdrPageView();
1853 SdrObjListIter aPageIter( pPage );
1854 bool bFound = true;
1856 // do all objects still exist
1857 const size_t nCount = m_aMark.GetMarkCount();
1858 for (size_t i = 0; i < nCount && bFound; ++i)
1860 SdrMark* pMark = m_aMark.GetMark(i);
1861 SdrObject* pObj = pMark->GetMarkedSdrObj();
1862 if (pObj->IsGroupObject())
1864 SdrObjListIter aIter(pObj->GetSubList());
1865 while (aIter.IsMore() && bFound)
1866 bFound = lcl_hasObject(aPageIter, aIter.Next());
1868 else
1869 bFound = lcl_hasObject(aPageIter, pObj);
1871 bFound = bFound && pCurPageView == pMark->GetPageView();
1874 if (bFound)
1876 // evaluate the LastObject
1877 if (nCount) // now mark the objects
1879 for (size_t i = 0; i < nCount; ++i)
1881 SdrMark* pMark = m_aMark.GetMark(i);
1882 SdrObject* pObj = pMark->GetMarkedSdrObj();
1883 if ( pObj->GetObjInventor() == SdrInventor::FmForm )
1884 if ( !m_pView->IsObjMarked( pObj ) )
1885 m_pView->MarkObj( pObj, pMark->GetPageView() );
1888 _rRestoredMarkList = m_aMark;
1891 m_aMark.Clear();
1894 void SAL_CALL FmXFormView::focusGained( const FocusEvent& /*e*/ )
1896 if ( m_xWindow.is() && m_pView )
1898 m_pView->SetMoveOutside( true, FmFormView::ImplAccess() );
1902 void SAL_CALL FmXFormView::focusLost( const FocusEvent& /*e*/ )
1904 // when switch the focus outside the office the mark didn't change
1905 // so we can not remove us as focus listener
1906 if ( m_xWindow.is() && m_pView )
1908 m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
1912 DocumentType FmXFormView::impl_getDocumentType() const
1914 if ( GetFormShell() && GetFormShell()->GetImpl() )
1915 return GetFormShell()->GetImpl()->getDocumentType_Lock();
1916 return eUnknownDocumentType;
1919 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */