1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
22 #include <fmvwimp.hxx>
23 #include <fmpgeimp.hxx>
24 #include <o3tl/string_view.hxx>
25 #include <svx/fmview.hxx>
26 #include <svx/fmpage.hxx>
27 #include <svx/svdovirt.hxx>
28 #include <svx/fmmodel.hxx>
30 #include <com/sun/star/awt/XDevice.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/form/Forms.hpp>
33 #include <com/sun/star/script/XEventAttacherManager.hpp>
34 #include <svx/fmtools.hxx>
36 #include <comphelper/property.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <toolkit/awt/vclxdevice.hxx>
39 #include <tools/debug.hxx>
40 #include <comphelper/diagnose_ex.hxx>
42 using namespace ::com::sun::star::uno
;
43 using namespace ::com::sun::star::awt
;
44 using namespace ::com::sun::star::lang
;
45 using namespace ::com::sun::star::form
;
46 using namespace ::com::sun::star::beans
;
47 using namespace ::com::sun::star::script
;
48 using namespace ::com::sun::star::container
;
49 using namespace ::svxform
;
54 const OUString
& rModelName
)
55 : SdrUnoObj(rSdrModel
, rModelName
)
57 ,m_pLastKnownRefDevice(nullptr)
59 // normally, this is done in SetUnoControlModel, but if the call happened in the base class ctor,
60 // then our incarnation of it was not called (since we were not constructed at this time).
61 impl_checkRefDevice_nothrow( true );
64 FmFormObj::FmFormObj(SdrModel
& rSdrModel
)
65 : SdrUnoObj(rSdrModel
, u
""_ustr
)
67 ,m_pLastKnownRefDevice(nullptr)
69 // Stuff that old SetModel also did:
70 impl_checkRefDevice_nothrow();
73 FmFormObj::FmFormObj(SdrModel
& rSdrModel
, FmFormObj
const & rSource
)
74 : SdrUnoObj(rSdrModel
, rSource
)
76 ,m_pLastKnownRefDevice(nullptr)
78 // Stuff that old SetModel also did:
79 impl_checkRefDevice_nothrow();
81 // If UnoControlModel is part of an event environment,
82 // events may assigned to it.
83 Reference
< XFormComponent
> xContent(rSource
.xUnoControlModel
, UNO_QUERY
);
86 Reference
< XEventAttacherManager
> xManager(xContent
->getParent(), UNO_QUERY
);
87 Reference
< XIndexAccess
> xManagerAsIndex(xManager
, UNO_QUERY
);
88 if (xManagerAsIndex
.is())
90 sal_Int32 nPos
= getElementPos( xManagerAsIndex
, xContent
);
92 aEvts
= xManager
->getScriptEvents( nPos
);
96 aEvts
= rSource
.aEvts
;
98 Reference
< XChild
> xSourceAsChild(rSource
.GetUnoControlModel(), UNO_QUERY
);
99 if (!xSourceAsChild
.is())
102 Reference
< XInterface
> xSourceContainer
= xSourceAsChild
->getParent();
104 m_xEnvironmentHistory
= css::form::Forms::create( comphelper::getProcessComponentContext() );
106 ensureModelEnv(xSourceContainer
, m_xEnvironmentHistory
);
107 m_aEventsHistory
= aEvts
;
108 // if we were clone there was a call to operator=, so aEvts are exactly the events we need here...
111 FmFormObj::~FmFormObj()
114 if (m_xEnvironmentHistory
.is())
115 m_xEnvironmentHistory
->dispose();
117 m_xEnvironmentHistory
= nullptr;
118 m_aEventsHistory
.realloc(0);
122 void FmFormObj::SetObjEnv(const Reference
< XIndexContainer
> & xForm
, const sal_Int32 nIdx
,
123 const Sequence
< ScriptEventDescriptor
>& rEvts
)
131 void FmFormObj::ClearObjEnv()
139 void FmFormObj::impl_checkRefDevice_nothrow( bool _force
)
141 const FmFormModel
* pFormModel
= dynamic_cast<FmFormModel
*>(&getSdrModelFromSdrObject());
142 if ( !pFormModel
|| !pFormModel
->ControlsUseRefDevice() )
145 OutputDevice
* pCurrentRefDevice
= pFormModel
->GetRefDevice();
146 if ( ( m_pLastKnownRefDevice
.get() == pCurrentRefDevice
) && !_force
)
149 Reference
< XControlModel
> xControlModel( GetUnoControlModel() );
150 if ( !xControlModel
.is() )
153 m_pLastKnownRefDevice
= pCurrentRefDevice
;
154 if ( !m_pLastKnownRefDevice
)
159 Reference
< XPropertySet
> xModelProps( GetUnoControlModel(), UNO_QUERY_THROW
);
160 Reference
< XPropertySetInfo
> xPropertyInfo( xModelProps
->getPropertySetInfo(), UNO_SET_THROW
);
162 static constexpr OUString sRefDevicePropName
= u
"ReferenceDevice"_ustr
;
163 if ( xPropertyInfo
->hasPropertyByName( sRefDevicePropName
) )
165 rtl::Reference
<VCLXDevice
> pUnoRefDevice
= new VCLXDevice
;
166 pUnoRefDevice
->SetOutputDevice( m_pLastKnownRefDevice
);
167 Reference
< XDevice
> xRefDevice( pUnoRefDevice
);
168 xModelProps
->setPropertyValue( sRefDevicePropName
, Any( xRefDevice
) );
171 catch( const Exception
& )
173 DBG_UNHANDLED_EXCEPTION("svx");
178 void FmFormObj::impl_isolateControlModel_nothrow()
182 Reference
< XChild
> xControlModel( GetUnoControlModel(), UNO_QUERY
);
183 if ( xControlModel
.is() )
185 Reference
< XIndexContainer
> xParent( xControlModel
->getParent(), UNO_QUERY
);
188 sal_Int32 nPos
= getElementPos( xParent
, xControlModel
);
189 xParent
->removeByIndex( nPos
);
193 catch( const Exception
& )
195 DBG_UNHANDLED_EXCEPTION("svx");
200 void FmFormObj::handlePageChange(SdrPage
* pOldPage
, SdrPage
* pNewPage
)
202 FmFormPage
* pOldFormPage(dynamic_cast< FmFormPage
* >(getSdrPageFromSdrObject()));
204 pOldFormPage
->GetImpl().formObjectRemoved( *this );
206 FmFormPage
* pNewFormPage
= dynamic_cast<FmFormPage
*>( pNewPage
);
209 // Maybe it makes sense to create an environment history here : if somebody set's our page to NULL, and we have a valid page before,
210 // me may want to remember our place within the old page. For this we could create a new m_xEnvironmentHistory to store it.
211 // So the next SetPage with a valid new page would restore that environment within the new page.
212 // But for the original Bug (#57300#) we don't need that, so I omit it here. Maybe this will be implemented later.
213 impl_isolateControlModel_nothrow();
214 SdrUnoObj::handlePageChange(pOldPage
, pNewPage
);
218 Reference
< css::form::XForms
> xNewPageForms
= pNewFormPage
->GetForms();
219 Reference
< XIndexContainer
> xNewParent
;
220 Sequence
< ScriptEventDescriptor
> aNewEvents
;
222 // calc the new parent for my model (within the new page's forms hierarchy)
223 // do we have a history ? (from :Clone)
224 if ( m_xEnvironmentHistory
.is() )
226 // the element in m_xEnvironmentHistory which is equivalent to my new parent (which (perhaps) has to be created within pNewPage->GetForms)
227 // is the right-most element in the tree.
228 Reference
< XIndexContainer
> xRightMostLeaf( m_xEnvironmentHistory
, UNO_QUERY_THROW
);
231 while ( xRightMostLeaf
->getCount() )
234 xRightMostLeaf
->getByIndex( xRightMostLeaf
->getCount() - 1 ),
239 xNewParent
.set( ensureModelEnv( xRightMostLeaf
, xNewPageForms
), UNO_QUERY_THROW
);
241 // we successfully cloned the environment in m_xEnvironmentHistory, so we can use m_aEventsHistory
242 // (which describes the events of our model at the moment m_xEnvironmentHistory was created)
243 aNewEvents
= m_aEventsHistory
;
245 catch( const Exception
& )
247 DBG_UNHANDLED_EXCEPTION("svx");
251 if ( !xNewParent
.is() )
253 // are we a valid part of our current page forms ?
254 Reference
< XIndexContainer
> xOldForms
;
256 xOldForms
.set( pOldFormPage
->GetForms(), UNO_QUERY_THROW
);
258 if ( xOldForms
.is() )
260 // search (upward from our model) for xOldForms
261 Reference
< XChild
> xSearch( GetUnoControlModel(), UNO_QUERY
);
264 if ( xSearch
== xOldForms
)
266 xSearch
.set( xSearch
->getParent(), UNO_QUERY
);
268 if ( xSearch
.is() ) // implies xSearch == xOldForms, which means we're a valid part of our current page forms hierarchy
270 Reference
< XChild
> xMeAsChild( GetUnoControlModel(), UNO_QUERY
);
271 xNewParent
.set( ensureModelEnv( xMeAsChild
->getParent(), xNewPageForms
), UNO_QUERY
);
273 if ( xNewParent
.is() )
277 // transfer the events from our (model's) parent to the new (model's) parent, too
278 Reference
< XEventAttacherManager
> xEventManager(xMeAsChild
->getParent(), UNO_QUERY
);
279 Reference
< XIndexAccess
> xManagerAsIndex(xEventManager
, UNO_QUERY
);
280 if (xManagerAsIndex
.is())
282 sal_Int32 nPos
= getElementPos(xManagerAsIndex
, xMeAsChild
);
284 aNewEvents
= xEventManager
->getScriptEvents(nPos
);
289 catch( const Exception
& )
291 DBG_UNHANDLED_EXCEPTION("svx");
299 SdrUnoObj::handlePageChange(pOldPage
, pNewPage
);
301 // place my model within the new parent container
304 Reference
< XFormComponent
> xMeAsFormComp(GetUnoControlModel(), UNO_QUERY
);
305 if (xMeAsFormComp
.is())
307 // check if I have another parent (and remove me, if necessary)
308 Reference
< XIndexContainer
> xOldParent(xMeAsFormComp
->getParent(), UNO_QUERY
);
311 sal_Int32 nPos
= getElementPos(xOldParent
, xMeAsFormComp
);
313 xOldParent
->removeByIndex(nPos
);
316 // and insert into the new container
317 xNewParent
->insertByIndex(xNewParent
->getCount(), Any(xMeAsFormComp
));
319 // transfer the events
320 if (aNewEvents
.hasElements())
324 Reference
< XEventAttacherManager
> xEventManager(xNewParent
, UNO_QUERY
);
325 Reference
< XIndexAccess
> xManagerAsIndex(xEventManager
, UNO_QUERY
);
326 if (xManagerAsIndex
.is())
328 sal_Int32 nPos
= getElementPos(xManagerAsIndex
, xMeAsFormComp
);
329 DBG_ASSERT(nPos
>= 0, "FmFormObj::SetPage : inserted but not present ?");
330 xEventManager
->registerScriptEvents(nPos
, aNewEvents
);
333 catch( const Exception
& )
335 DBG_UNHANDLED_EXCEPTION("svx");
343 if (m_xEnvironmentHistory
.is())
344 m_xEnvironmentHistory
->dispose();
346 m_xEnvironmentHistory
= nullptr;
347 m_aEventsHistory
.realloc(0);
349 pNewFormPage
->GetImpl().formObjectInserted( *this );
352 SdrInventor
FmFormObj::GetObjInventor() const
354 return SdrInventor::FmForm
;
357 SdrObjKind
FmFormObj::GetObjIdentifier() const
359 return SdrObjKind::UNO
;
362 rtl::Reference
<SdrObject
> FmFormObj::CloneSdrObject(SdrModel
& rTargetModel
) const
364 return new FmFormObj(rTargetModel
, *this);
367 void FmFormObj::NbcReformatText()
369 impl_checkRefDevice_nothrow();
370 SdrUnoObj::NbcReformatText();
376 OUString
lcl_getFormComponentAccessPath(const Reference
< XInterface
>& _xElement
, Reference
< XInterface
>& _rTopLevelElement
)
378 Reference
< css::form::XFormComponent
> xChild(_xElement
, UNO_QUERY
);
379 Reference
< css::container::XIndexAccess
> xParent
;
381 xParent
.set(xChild
->getParent(), UNO_QUERY
);
383 // while the current content is a form
387 // get the content's relative pos within its parent container
388 sal_Int32 nPos
= getElementPos(xParent
, xChild
);
390 // prepend this current relative pos
391 OUString sCurrentIndex
= OUString::number(nPos
);
392 if (!sReturn
.isEmpty())
394 sCurrentIndex
+= "\\" + sReturn
;
397 sReturn
= sCurrentIndex
;
400 xChild
.set(xParent
, css::uno::UNO_QUERY
);
402 xParent
.set(xChild
->getParent(), UNO_QUERY
);
405 _rTopLevelElement
= xParent
;
411 Reference
< XInterface
> FmFormObj::ensureModelEnv(const Reference
< XInterface
> & _rSourceContainer
, const Reference
<css::form::XForms
>& _rTopLevelDestContainer
)
413 Reference
< XInterface
> xTopLevelSource
;
414 OUString sAccessPath
= lcl_getFormComponentAccessPath(_rSourceContainer
, xTopLevelSource
);
415 if (!xTopLevelSource
.is())
416 // something went wrong, maybe _rSourceContainer isn't part of a valid forms hierarchy
417 return Reference
< XInterface
> ();
419 Reference
< XIndexContainer
> xDestContainer(_rTopLevelDestContainer
, UNO_QUERY_THROW
);
420 Reference
< XIndexContainer
> xSourceContainer(xTopLevelSource
, UNO_QUERY
);
421 DBG_ASSERT(xSourceContainer
.is(), "FmFormObj::ensureModelEnv : the top level source is invalid !");
423 sal_Int32 nTokIndex
= 0;
426 std::u16string_view aToken
= o3tl::getToken(sAccessPath
, 0, '\\', nTokIndex
);
427 sal_uInt16 nIndex
= static_cast<sal_uInt16
>(o3tl::toInt32(aToken
));
429 // get the DSS of the source form (we have to find an equivalent for)
430 DBG_ASSERT(nIndex
<xSourceContainer
->getCount(), "FmFormObj::ensureModelEnv : invalid access path !");
431 Reference
< XPropertySet
> xSourceForm
;
432 xSourceContainer
->getByIndex(nIndex
) >>= xSourceForm
;
433 DBG_ASSERT(xSourceForm
.is(), "FmFormObj::ensureModelEnv : invalid source form !");
435 Any aSrcCursorSource
, aSrcCursorSourceType
, aSrcDataSource
;
436 DBG_ASSERT(::comphelper::hasProperty(FM_PROP_COMMAND
, xSourceForm
) && ::comphelper::hasProperty(FM_PROP_COMMANDTYPE
, xSourceForm
)
437 && ::comphelper::hasProperty(FM_PROP_DATASOURCE
, xSourceForm
), "FmFormObj::ensureModelEnv : invalid access path or invalid form (missing props) !");
438 // the parent access path should refer to a row set
441 aSrcCursorSource
= xSourceForm
->getPropertyValue(FM_PROP_COMMAND
);
442 aSrcCursorSourceType
= xSourceForm
->getPropertyValue(FM_PROP_COMMANDTYPE
);
443 aSrcDataSource
= xSourceForm
->getPropertyValue(FM_PROP_DATASOURCE
);
447 OSL_FAIL("FmFormObj::ensureModelEnv : could not retrieve a source DSS !");
451 // calc the number of (source) form siblings with the same DSS
452 Reference
< XPropertySet
> xCurrentSourceForm
, xCurrentDestForm
;
453 sal_Int16 nCurrentSourceIndex
= 0;
454 sal_Int32 nCurrentDestIndex
= 0;
455 while (nCurrentSourceIndex
<= nIndex
)
457 bool bEqualDSS
= false;
458 while (!bEqualDSS
) // (we don't have to check nCurrentSourceIndex here : it's bound by nIndex)
460 xSourceContainer
->getByIndex(nCurrentSourceIndex
) >>= xCurrentSourceForm
;
461 DBG_ASSERT(xCurrentSourceForm
.is(), "FmFormObj::ensureModelEnv : invalid form ancestor (2) !");
463 if (::comphelper::hasProperty(FM_PROP_DATASOURCE
, xCurrentSourceForm
))
467 if ( xCurrentSourceForm
->getPropertyValue(FM_PROP_COMMAND
) == aSrcCursorSource
468 && xCurrentSourceForm
->getPropertyValue(FM_PROP_COMMANDTYPE
) == aSrcCursorSourceType
469 && xCurrentSourceForm
->getPropertyValue(FM_PROP_DATASOURCE
) == aSrcDataSource
477 TOOLS_WARN_EXCEPTION("svx.form",
478 "exception while getting a sibling's DSS !");
482 ++nCurrentSourceIndex
;
485 DBG_ASSERT(bEqualDSS
, "FmFormObj::ensureModelEnv : found no source form !");
486 // ??? at least the nIndex-th one should have been found ???
488 // now search the next one with the given DSS (within the destination container)
490 while (!bEqualDSS
&& (nCurrentDestIndex
< xDestContainer
->getCount()))
492 xDestContainer
->getByIndex(nCurrentDestIndex
) >>= xCurrentDestForm
;
493 DBG_ASSERT(xCurrentDestForm
.is(), "FmFormObj::ensureModelEnv : invalid destination form !");
495 if (::comphelper::hasProperty(FM_PROP_DATASOURCE
, xCurrentDestForm
))
499 if ( xCurrentDestForm
->getPropertyValue(FM_PROP_COMMAND
) == aSrcCursorSource
500 && xCurrentDestForm
->getPropertyValue(FM_PROP_COMMANDTYPE
) == aSrcCursorSourceType
501 && xCurrentDestForm
->getPropertyValue(FM_PROP_DATASOURCE
) == aSrcDataSource
509 TOOLS_WARN_EXCEPTION("svx.form",
510 "exception while getting a destination DSS !");
518 { // There is at least one more source form with the given DSS than destination forms are.
522 // create and insert (into the destination) a copy of the form
523 xCurrentDestForm
.set(
524 ::comphelper::getProcessServiceFactory()->createInstance(u
"com.sun.star.form.component.DataForm"_ustr
),
526 ::comphelper::copyProperties( xCurrentSourceForm
, xCurrentDestForm
);
528 DBG_ASSERT(nCurrentDestIndex
== xDestContainer
->getCount(), "FmFormObj::ensureModelEnv : something went wrong with the numbers !");
529 xDestContainer
->insertByIndex(nCurrentDestIndex
, Any(xCurrentDestForm
));
532 // like nCurrentSourceIndex, nCurrentDestIndex now points 'behind' the form it actually means
536 OSL_FAIL("FmFormObj::ensureModelEnv : something went seriously wrong while creating a new form !");
537 // no more options anymore ...
538 return Reference
< XInterface
> ();
544 // now xCurrentDestForm is a form equivalent to xSourceForm (which means they have the same DSS and the same number
545 // of left siblings with the same DSS, which counts for all their ancestors, too)
548 xDestContainer
.set(xCurrentDestForm
, UNO_QUERY
);
549 xSourceContainer
.set(xSourceForm
, UNO_QUERY
);
550 DBG_ASSERT(xDestContainer
.is() && xSourceContainer
.is(), "FmFormObj::ensureModelEnv : invalid container !");
552 while ( nTokIndex
>= 0 );
554 return Reference
<XInterface
>( xDestContainer
, UNO_QUERY
);
557 FmFormObj
* FmFormObj::GetFormObject( SdrObject
* _pSdrObject
)
559 FmFormObj
* pFormObject
= dynamic_cast< FmFormObj
* >( _pSdrObject
);
562 SdrVirtObj
* pVirtualObject
= dynamic_cast< SdrVirtObj
* >( _pSdrObject
);
563 if ( pVirtualObject
)
564 pFormObject
= dynamic_cast< FmFormObj
* >( &pVirtualObject
->ReferencedObj() );
570 const FmFormObj
* FmFormObj::GetFormObject( const SdrObject
* _pSdrObject
)
572 const FmFormObj
* pFormObject
= dynamic_cast< const FmFormObj
* >( _pSdrObject
);
575 const SdrVirtObj
* pVirtualObject
= dynamic_cast< const SdrVirtObj
* >( _pSdrObject
);
576 if ( pVirtualObject
)
577 pFormObject
= dynamic_cast< const FmFormObj
* >( &pVirtualObject
->GetReferencedObj() );
583 void FmFormObj::SetUnoControlModel( const Reference
< css::awt::XControlModel
>& _rxModel
)
585 SdrUnoObj::SetUnoControlModel( _rxModel
);
587 FmFormPage
* pFormPage(dynamic_cast< FmFormPage
* >(getSdrPageFromSdrObject()));
589 pFormPage
->GetImpl().formModelAssigned( *this );
591 impl_checkRefDevice_nothrow( true );
595 bool FmFormObj::EndCreate( SdrDragStat
& rStat
, SdrCreateCmd eCmd
)
597 bool bResult
= SdrUnoObj::EndCreate(rStat
, eCmd
);
598 if ( bResult
&& SdrCreateCmd::ForceEnd
== eCmd
&& rStat
.GetView() )
600 FmFormPage
* pFormPage(dynamic_cast< FmFormPage
* >(getSdrPageFromSdrObject()));
602 if (nullptr != pFormPage
)
606 Reference
< XFormComponent
> xContent( xUnoControlModel
, UNO_QUERY_THROW
);
607 Reference
< XForm
> xParentForm( xContent
->getParent(), UNO_QUERY
);
609 Reference
< XIndexContainer
> xFormToInsertInto
;
611 if ( !xParentForm
.is() )
612 { // model is not yet part of a form component hierarchy
613 xParentForm
.set( pFormPage
->GetImpl().findPlaceInFormComponentHierarchy( xContent
), UNO_SET_THROW
);
614 xFormToInsertInto
.set( xParentForm
, UNO_QUERY_THROW
);
617 FmFormPageImpl::setUniqueName( xContent
, xParentForm
);
619 if ( xFormToInsertInto
.is() )
620 xFormToInsertInto
->insertByIndex( xFormToInsertInto
->getCount(), Any( xContent
) );
622 catch( const Exception
& )
624 DBG_UNHANDLED_EXCEPTION("svx");
628 FmFormView
* pView( dynamic_cast< FmFormView
* >( rStat
.GetView() ) );
629 FmXFormView
* pViewImpl
= pView
? pView
->GetImpl() : nullptr;
630 OSL_ENSURE( pViewImpl
, "FmFormObj::EndCreate: no view!?" );
632 pViewImpl
->onCreatedFormObject( *this );
638 void FmFormObj::BrkCreate( SdrDragStat
& rStat
)
640 SdrUnoObj::BrkCreate( rStat
);
641 impl_isolateControlModel_nothrow();
643 FmFormView
* pView( dynamic_cast< FmFormView
* >( rStat
.GetView() ) );
644 FmXFormView
* pViewImpl
= pView
? pView
->GetImpl() : nullptr;
645 OSL_ENSURE( pViewImpl
, "FmFormObj::EndCreate: no view!?" );
647 pViewImpl
->breakCreateFormObject();
651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */