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 .
20 #include <sal/config.h>
23 #include <string_view>
25 #include <sal/macros.h>
27 #include <fmpgeimp.hxx>
28 #include <svx/svditer.hxx>
31 #include <svx/strings.hrc>
32 #include <svx/dialmgr.hxx>
33 #include <svx/fmmodel.hxx>
34 #include <svx/fmpage.hxx>
36 #include <com/sun/star/util/XModifyBroadcaster.hpp>
37 #include <com/sun/star/beans/PropertyAttribute.hpp>
38 #include <com/sun/star/container/XContainer.hpp>
39 #include <com/sun/star/container/XContainerListener.hpp>
40 #include <com/sun/star/script/XEventAttacherManager.hpp>
41 #include <com/sun/star/form/binding/XBindableValue.hpp>
42 #include <com/sun/star/form/binding/XListEntrySink.hpp>
43 #include <com/sun/star/sdbc/XConnection.hpp>
44 #include <com/sun/star/uno/XComponentContext.hpp>
46 #include <svx/fmtools.hxx>
47 #include <tools/debug.hxx>
48 #include <tools/diagnose_ex.h>
49 #include <sfx2/objsh.hxx>
50 #include <sfx2/event.hxx>
51 #include <osl/mutex.hxx>
52 #include <comphelper/property.hxx>
53 #include <comphelper/types.hxx>
54 #include <connectivity/dbtools.hxx>
55 #include <vcl/svapp.hxx>
57 using namespace ::com::sun::star::uno
;
58 using namespace ::com::sun::star::awt
;
59 using namespace ::com::sun::star::beans
;
60 using namespace ::com::sun::star::container
;
61 using namespace ::com::sun::star::script
;
62 using namespace ::com::sun::star::lang
;
63 using namespace ::com::sun::star::form
;
64 using namespace ::com::sun::star::util
;
65 using namespace ::com::sun::star::form::binding
;
66 using namespace ::com::sun::star::sdbc
;
67 using namespace ::svxform
;
68 using namespace ::dbtools
;
71 #include <com/sun/star/script/XScriptListener.hpp>
72 #include <comphelper/processfactory.hxx>
73 #include <cppuhelper/implbase.hxx>
77 class ScriptEventListenerWrapper
: public cppu::WeakImplHelper
< XScriptListener
>
80 /// @throws css::uno::RuntimeException
81 explicit ScriptEventListenerWrapper( FmFormModel
& _rModel
)
83 ,m_attemptedListenerCreation( false )
88 virtual void SAL_CALL
disposing(const EventObject
& ) override
{}
91 virtual void SAL_CALL
firing(const ScriptEvent
& evt
) override
93 attemptListenerCreation();
94 if ( m_vbaListener
.is() )
96 m_vbaListener
->firing( evt
);
100 virtual Any SAL_CALL
approveFiring(const ScriptEvent
& evt
) override
102 attemptListenerCreation();
103 if ( m_vbaListener
.is() )
105 return m_vbaListener
->approveFiring( evt
);
111 void attemptListenerCreation()
113 if ( m_attemptedListenerCreation
)
115 m_attemptedListenerCreation
= true;
119 css::uno::Reference
<css::uno::XComponentContext
> context(
120 comphelper::getProcessComponentContext());
121 Reference
< XScriptListener
> const xScriptListener(
122 context
->getServiceManager()->createInstanceWithContext(
123 "ooo.vba.EventListener", context
),
125 Reference
< XPropertySet
> const xListenerProps( xScriptListener
, UNO_QUERY_THROW
);
126 // SfxObjectShellRef is good here since the model controls the lifetime of the shell
127 SfxObjectShellRef
const xObjectShell
= m_rModel
.GetObjectShell();
128 ENSURE_OR_THROW( xObjectShell
.is(), "no object shell!" );
129 xListenerProps
->setPropertyValue("Model", makeAny( xObjectShell
->GetModel() ) );
131 m_vbaListener
= xScriptListener
;
133 catch( Exception
const & )
135 DBG_UNHANDLED_EXCEPTION("svx");
138 FmFormModel
& m_rModel
;
139 Reference
< XScriptListener
> m_vbaListener
;
140 bool m_attemptedListenerCreation
;
146 // some helper structs for caching property infos
150 bool bIsTransientOrReadOnly
: 1; // the property is transient or read-only, thus we need no undo action for it
151 bool bIsValueProperty
: 1; // the property is the special value property, thus it may be handled
152 // as if it's transient or persistent
155 struct PropertySetInfo
157 typedef std::map
<OUString
, PropertyInfo
> AllProperties
;
159 AllProperties aProps
; // all properties of this set which we know so far
160 bool bHasEmptyControlSource
; // sal_True -> the set has a DataField property, and the current value is an empty string
161 // sal_False -> the set has _no_ such property or its value isn't empty
166 typedef std::map
<Reference
< XPropertySet
>, PropertySetInfo
> PropertySetInfoCache
;
169 static OUString static_STR_UNDO_PROPERTY
;
172 FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel
& _rModel
)
174 ,m_pPropertySetCache( nullptr )
175 ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel
) )
178 ,m_bDisposed( false )
182 m_vbaListener
= new ScriptEventListenerWrapper( _rModel
);
189 FmXUndoEnvironment::~FmXUndoEnvironment()
191 if ( !m_bDisposed
) // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
192 m_pScriptingEnv
->dispose();
194 if (m_pPropertySetCache
)
195 delete static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
198 void FmXUndoEnvironment::dispose()
200 OSL_ENSURE( !m_bDisposed
, "FmXUndoEnvironment::dispose: disposed twice?" );
206 sal_uInt16 nCount
= rModel
.GetPageCount();
208 for (i
= 0; i
< nCount
; i
++)
210 FmFormPage
* pPage
= dynamic_cast<FmFormPage
*>( rModel
.GetPage(i
) );
213 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
215 RemoveElement( xForms
);
219 nCount
= rModel
.GetMasterPageCount();
220 for (i
= 0; i
< nCount
; i
++)
222 FmFormPage
* pPage
= dynamic_cast<FmFormPage
*>( rModel
.GetMasterPage(i
) );
225 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
227 RemoveElement( xForms
);
233 OSL_PRECOND( rModel
.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
234 if ( rModel
.GetObjectShell() )
235 EndListening( *rModel
.GetObjectShell() );
237 if ( IsListening( rModel
) )
238 EndListening( rModel
);
240 m_pScriptingEnv
->dispose();
246 void FmXUndoEnvironment::ModeChanged()
248 OSL_PRECOND( rModel
.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
249 if ( !rModel
.GetObjectShell() )
252 if (bReadOnly
== (rModel
.GetObjectShell()->IsReadOnly() || rModel
.GetObjectShell()->IsReadOnlyUI()))
255 bReadOnly
= !bReadOnly
;
257 sal_uInt16 nCount
= rModel
.GetPageCount();
259 for (i
= 0; i
< nCount
; i
++)
261 FmFormPage
* pPage
= dynamic_cast<FmFormPage
*>( rModel
.GetPage(i
) );
264 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
266 TogglePropertyListening( xForms
);
270 nCount
= rModel
.GetMasterPageCount();
271 for (i
= 0; i
< nCount
; i
++)
273 FmFormPage
* pPage
= dynamic_cast<FmFormPage
*>( rModel
.GetMasterPage(i
) );
276 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
278 TogglePropertyListening( xForms
);
283 StartListening(rModel
);
285 EndListening(rModel
);
289 void FmXUndoEnvironment::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
291 if (rHint
.GetId() == SfxHintId::ThisIsAnSdrHint
)
293 const SdrHint
* pSdrHint
= static_cast<const SdrHint
*>(&rHint
);
294 switch (pSdrHint
->GetKind())
296 case SdrHintKind::ObjectInserted
:
298 SdrObject
* pSdrObj
= const_cast<SdrObject
*>(pSdrHint
->GetObject());
301 case SdrHintKind::ObjectRemoved
:
303 SdrObject
* pSdrObj
= const_cast<SdrObject
*>(pSdrHint
->GetObject());
311 else if (rHint
.GetId() != SfxHintId::NONE
)
313 switch (rHint
.GetId())
315 case SfxHintId::Dying
:
317 rModel
.SetObjectShell( nullptr );
319 case SfxHintId::ModeChanged
:
325 else if (const SfxEventHint
* pEventHint
= dynamic_cast<const SfxEventHint
*>(&rHint
))
327 switch (pEventHint
->GetEventId())
329 case SfxEventHintId::CreateDoc
:
330 case SfxEventHintId::OpenDoc
:
338 void FmXUndoEnvironment::Inserted(SdrObject
* pObj
)
340 if (pObj
->GetObjInventor() == SdrInventor::FmForm
)
342 FmFormObj
* pFormObj
= dynamic_cast<FmFormObj
*>( pObj
);
343 Inserted( pFormObj
);
345 else if (pObj
->IsGroupObject())
347 SdrObjListIter
aIter(pObj
->GetSubList());
348 while ( aIter
.IsMore() )
349 Inserted( aIter
.Next() );
356 bool lcl_searchElement(const Reference
< XIndexAccess
>& xCont
, const Reference
< XInterface
>& xElement
)
358 if (!xCont
.is() || !xElement
.is())
361 sal_Int32 nCount
= xCont
->getCount();
362 Reference
< XInterface
> xComp
;
363 for (sal_Int32 i
= 0; i
< nCount
; i
++)
367 xCont
->getByIndex(i
) >>= xComp
;
370 if ( xElement
== xComp
)
374 Reference
< XIndexAccess
> xCont2(xComp
, UNO_QUERY
);
375 if (xCont2
.is() && lcl_searchElement(xCont2
, xElement
))
380 catch(const Exception
&)
382 DBG_UNHANDLED_EXCEPTION("svx");
390 void FmXUndoEnvironment::Inserted(FmFormObj
* pObj
)
392 DBG_ASSERT( pObj
, "FmXUndoEnvironment::Inserted: invalid object!" );
396 // is the control still assigned to a form
397 Reference
< XInterface
> xModel(pObj
->GetUnoControlModel(), UNO_QUERY
);
398 Reference
< XFormComponent
> xContent(xModel
, UNO_QUERY
);
399 if (!(xContent
.is() && pObj
->getSdrPageFromSdrObject()))
402 // if the component doesn't belong to a form, yet, find one to insert into
403 if (!xContent
->getParent().is())
407 const Reference
< XIndexContainer
>& xObjectParent
= pObj
->GetOriginalParent();
409 FmFormPage
& rPage(dynamic_cast< FmFormPage
& >( *pObj
->getSdrPageFromSdrObject()));
410 Reference
< XIndexAccess
> xForms( rPage
.GetForms(), UNO_QUERY_THROW
);
412 Reference
< XIndexContainer
> xNewParent
;
413 Reference
< XForm
> xForm
;
415 if ( lcl_searchElement( xForms
, xObjectParent
) )
417 // the form which was the parent of the object when it was removed is still
418 // part of the form component hierarchy of the current page
419 xNewParent
= xObjectParent
;
420 xForm
.set( xNewParent
, UNO_QUERY_THROW
);
421 nPos
= ::std::min( pObj
->GetOriginalIndex(), xNewParent
->getCount() );
425 xForm
.set( rPage
.GetImpl().findPlaceInFormComponentHierarchy( xContent
), UNO_SET_THROW
);
426 xNewParent
.set( xForm
, UNO_QUERY_THROW
);
427 nPos
= xNewParent
->getCount();
430 FmFormPageImpl::setUniqueName( xContent
, xForm
);
431 xNewParent
->insertByIndex( nPos
, makeAny( xContent
) );
433 Reference
< XEventAttacherManager
> xManager( xNewParent
, UNO_QUERY_THROW
);
434 xManager
->registerScriptEvents( nPos
, pObj
->GetOriginalEvents() );
436 catch( const Exception
& )
438 DBG_UNHANDLED_EXCEPTION("svx");
447 void FmXUndoEnvironment::Removed(SdrObject
* pObj
)
449 if ( pObj
->IsVirtualObj() )
450 // for virtual objects, we've already been notified of the removal of the master
451 // object, which is sufficient here
454 if (pObj
->GetObjInventor() == SdrInventor::FmForm
)
456 FmFormObj
* pFormObj
= dynamic_cast<FmFormObj
*>( pObj
);
459 else if (pObj
->IsGroupObject())
461 SdrObjListIter
aIter(pObj
->GetSubList());
462 while ( aIter
.IsMore() )
463 Removed( aIter
.Next() );
468 void FmXUndoEnvironment::Removed(FmFormObj
* pObj
)
470 DBG_ASSERT( pObj
, "FmXUndoEnvironment::Removed: invalid object!" );
474 // is the control still assigned to a form
475 Reference
< XFormComponent
> xContent(pObj
->GetUnoControlModel(), UNO_QUERY
);
479 // The object is taken out of a list.
480 // If a father exists, the object is removed at the father and
481 // noted at the FormObject!
483 // If the object is reinserted and a parent exists, this parent is set though.
484 Reference
< XIndexContainer
> xForm(xContent
->getParent(), UNO_QUERY
);
488 Reference
< XIndexAccess
> xIndexAccess(xForm
.get());
489 // determine which position the child was at
490 const sal_Int32 nPos
= getElementPos(xIndexAccess
, xContent
);
494 Sequence
< ScriptEventDescriptor
> aEvts
;
495 Reference
< XEventAttacherManager
> xManager(xForm
, UNO_QUERY
);
497 aEvts
= xManager
->getScriptEvents(nPos
);
501 pObj
->SetObjEnv(xForm
, nPos
, aEvts
);
502 xForm
->removeByIndex(nPos
);
506 DBG_UNHANDLED_EXCEPTION("svx");
512 void SAL_CALL
FmXUndoEnvironment::disposing(const EventObject
& e
)
514 // check if it's an object we have cached information about
515 if (m_pPropertySetCache
)
517 Reference
< XPropertySet
> xSourceSet(e
.Source
, UNO_QUERY
);
520 PropertySetInfoCache
* pCache
= static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
521 PropertySetInfoCache::iterator aSetPos
= pCache
->find(xSourceSet
);
522 if (aSetPos
!= pCache
->end())
523 pCache
->erase(aSetPos
);
528 // XPropertyChangeListener
530 void SAL_CALL
FmXUndoEnvironment::propertyChange(const PropertyChangeEvent
& evt
)
532 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
536 Reference
< XPropertySet
> xSet(evt
.Source
, UNO_QUERY
);
540 // if it's a "default value" property of a control model, set the according "value" property
541 static const std::u16string_view pDefaultValueProperties
[] = {
542 u
"" FM_PROP_DEFAULT_TEXT
, u
"" FM_PROP_DEFAULTCHECKED
, u
"" FM_PROP_DEFAULT_DATE
, u
"" FM_PROP_DEFAULT_TIME
,
543 u
"" FM_PROP_DEFAULT_VALUE
, u
"" FM_PROP_DEFAULT_SELECT_SEQ
, u
"" FM_PROP_EFFECTIVE_DEFAULT
545 static const std::u16string_view aValueProperties
[] = {
546 u
"" FM_PROP_TEXT
, u
"" FM_PROP_STATE
, u
"" FM_PROP_DATE
, u
"" FM_PROP_TIME
,
547 u
"" FM_PROP_VALUE
, u
"" FM_PROP_SELECT_SEQ
, u
"" FM_PROP_EFFECTIVE_VALUE
549 sal_Int32 nDefaultValueProps
= SAL_N_ELEMENTS(pDefaultValueProperties
);
550 OSL_ENSURE(SAL_N_ELEMENTS(aValueProperties
) == nDefaultValueProps
,
551 "FmXUndoEnvironment::propertyChange: inconsistence!");
552 for (sal_Int32 i
=0; i
<nDefaultValueProps
; ++i
)
554 if (evt
.PropertyName
== pDefaultValueProperties
[i
])
558 xSet
->setPropertyValue(aValueProperties
[i
], evt
.NewValue
);
560 catch(const Exception
&)
562 OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
567 // no Undo for transient and readonly props. But unfortunately "transient" is not only that the
568 // "transient" flag is set for the property in question, instead it is somewhat more complex
569 // Transience criterions are:
570 // - the "transient" flag is set for the property
571 // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
572 // to a database column. Note that it doesn't matter here whether the control actually
573 // *is* bound to a column
574 // - OR the control is bound to an external value via XBindableValue/XValueBinding
575 // which does not have a "ExternalData" property being <TRUE/>
577 if (!m_pPropertySetCache
)
578 m_pPropertySetCache
= new PropertySetInfoCache
;
579 PropertySetInfoCache
* pCache
= static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
581 // let's see if we know something about the set
582 PropertySetInfoCache::iterator aSetPos
= pCache
->find(xSet
);
583 if (aSetPos
== pCache
->end())
585 PropertySetInfo aNewEntry
;
586 if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE
, xSet
))
588 aNewEntry
.bHasEmptyControlSource
= false;
594 Any aCurrentControlSource
= xSet
->getPropertyValue(FM_PROP_CONTROLSOURCE
);
595 aNewEntry
.bHasEmptyControlSource
= !aCurrentControlSource
.hasValue() || ::comphelper::getString(aCurrentControlSource
).isEmpty();
597 catch(const Exception
&)
599 DBG_UNHANDLED_EXCEPTION("svx");
602 aSetPos
= pCache
->emplace(xSet
,aNewEntry
).first
;
603 DBG_ASSERT(aSetPos
!= pCache
->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
606 { // is it the DataField property ?
607 if (evt
.PropertyName
== FM_PROP_CONTROLSOURCE
)
609 aSetPos
->second
.bHasEmptyControlSource
= !evt
.NewValue
.hasValue() || ::comphelper::getString(evt
.NewValue
).isEmpty();
613 // now we have access to the cached info about the set
614 // let's see what we know about the property
615 PropertySetInfo::AllProperties
& rPropInfos
= aSetPos
->second
.aProps
;
616 PropertySetInfo::AllProperties::iterator aPropertyPos
= rPropInfos
.find(evt
.PropertyName
);
617 if (aPropertyPos
== rPropInfos
.end())
618 { // nothing 'til now ... have to change this...
619 PropertyInfo aNewEntry
;
622 sal_Int32 nAttributes
= xSet
->getPropertySetInfo()->getPropertyByName(evt
.PropertyName
).Attributes
;
623 aNewEntry
.bIsTransientOrReadOnly
= ((nAttributes
& PropertyAttribute::READONLY
) != 0) || ((nAttributes
& PropertyAttribute::TRANSIENT
) != 0);
625 // check if it is the special "DataFieldProperty"
626 aNewEntry
.bIsValueProperty
= false;
629 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY
, xSet
))
631 Any aControlSourceProperty
= xSet
->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY
);
632 OUString sControlSourceProperty
;
633 aControlSourceProperty
>>= sControlSourceProperty
;
635 aNewEntry
.bIsValueProperty
= (sControlSourceProperty
== evt
.PropertyName
);
638 catch(const Exception
&)
640 DBG_UNHANDLED_EXCEPTION("svx");
643 // insert the new entry
644 aPropertyPos
= rPropInfos
.emplace(evt
.PropertyName
,aNewEntry
).first
;
645 DBG_ASSERT(aPropertyPos
!= rPropInfos
.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
648 // now we have access to the cached info about the property affected
649 // and are able to decide whether or not we need an undo action
651 bool bAddUndoAction
= rModel
.IsUndoEnabled();
652 // no UNDO for transient/readonly properties
653 if ( bAddUndoAction
&& aPropertyPos
->second
.bIsTransientOrReadOnly
)
654 bAddUndoAction
= false;
656 if ( bAddUndoAction
&& aPropertyPos
->second
.bIsValueProperty
)
658 // no UNDO when the "value" property changes, but the ControlSource is non-empty
659 // (in this case the control is intended to be bound to a database column)
660 if ( !aSetPos
->second
.bHasEmptyControlSource
)
661 bAddUndoAction
= false;
663 // no UNDO if the control is currently bound to an external value
664 if ( bAddUndoAction
)
666 Reference
< XBindableValue
> xBindable( evt
.Source
, UNO_QUERY
);
667 Reference
< XValueBinding
> xBinding
;
668 if ( xBindable
.is() )
669 xBinding
= xBindable
->getValueBinding();
671 Reference
< XPropertySet
> xBindingProps
;
672 Reference
< XPropertySetInfo
> xBindingPropsPSI
;
673 if ( xBindable
.is() )
674 xBindingProps
.set( xBinding
, UNO_QUERY
);
675 if ( xBindingProps
.is() )
676 xBindingPropsPSI
= xBindingProps
->getPropertySetInfo();
677 // TODO: we should cache all those things, else this might be too expensive.
678 // However, this requires we're notified of changes in the value binding
680 static const char s_sExternalData
[] = "ExternalData";
681 if ( xBindingPropsPSI
.is() && xBindingPropsPSI
->hasPropertyByName( s_sExternalData
) )
683 bool bExternalData
= true;
684 OSL_VERIFY( xBindingProps
->getPropertyValue( s_sExternalData
) >>= bExternalData
);
685 bAddUndoAction
= !bExternalData
;
688 bAddUndoAction
= !xBinding
.is();
692 if ( bAddUndoAction
&& ( evt
.PropertyName
== FM_PROP_STRINGITEMLIST
) )
694 Reference
< XListEntrySink
> xSink( evt
.Source
, UNO_QUERY
);
695 if ( xSink
.is() && xSink
->getListEntrySource().is() )
696 // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
697 bAddUndoAction
= false;
700 if ( bAddUndoAction
)
703 // TODO: this is a potential race condition: two threads here could in theory
704 // add their undo actions out-of-order
706 SolarMutexGuard aSolarGuard
;
707 rModel
.AddUndo(std::make_unique
<FmUndoPropertyAction
>(rModel
, evt
));
712 // if it's the DataField property we may have to adjust our cache
713 if (m_pPropertySetCache
&& evt
.PropertyName
== FM_PROP_CONTROLSOURCE
)
715 Reference
< XPropertySet
> xSet(evt
.Source
, UNO_QUERY
);
716 PropertySetInfoCache
* pCache
= static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
717 PropertySetInfo
& rSetInfo
= (*pCache
)[xSet
];
718 rSetInfo
.bHasEmptyControlSource
= !evt
.NewValue
.hasValue() || ::comphelper::getString(evt
.NewValue
).isEmpty();
723 // XContainerListener
725 void SAL_CALL
FmXUndoEnvironment::elementInserted(const ContainerEvent
& evt
)
727 SolarMutexGuard aSolarGuard
;
728 ::osl::MutexGuard
aGuard( m_aMutex
);
730 // new object for listening
731 Reference
< XInterface
> xIface
;
732 evt
.Element
>>= xIface
;
733 OSL_ENSURE(xIface
.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
740 void FmXUndoEnvironment::implSetModified()
742 if ( !IsLocked() && rModel
.GetObjectShell() )
744 rModel
.GetObjectShell()->SetModified();
749 void SAL_CALL
FmXUndoEnvironment::elementReplaced(const ContainerEvent
& evt
)
751 SolarMutexGuard aSolarGuard
;
752 ::osl::MutexGuard
aGuard( m_aMutex
);
754 Reference
< XInterface
> xIface
;
755 evt
.ReplacedElement
>>= xIface
;
756 OSL_ENSURE(xIface
.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
757 RemoveElement(xIface
);
759 evt
.Element
>>= xIface
;
766 void SAL_CALL
FmXUndoEnvironment::elementRemoved(const ContainerEvent
& evt
)
768 SolarMutexGuard aSolarGuard
;
769 ::osl::MutexGuard
aGuard( m_aMutex
);
771 Reference
< XInterface
> xIface( evt
.Element
, UNO_QUERY
);
772 OSL_ENSURE(xIface
.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
773 RemoveElement(xIface
);
779 void SAL_CALL
FmXUndoEnvironment::modified( const EventObject
& /*aEvent*/ )
785 void FmXUndoEnvironment::AddForms(const Reference
< XNameContainer
> & rForms
)
788 AddElement(Reference
<XInterface
>( rForms
, UNO_QUERY
));
793 void FmXUndoEnvironment::RemoveForms(const Reference
< XNameContainer
> & rForms
)
796 RemoveElement(Reference
<XInterface
>( rForms
, UNO_QUERY
));
801 void FmXUndoEnvironment::TogglePropertyListening(const Reference
< XInterface
> & Element
)
803 // listen at the container
804 Reference
< XIndexContainer
> xContainer(Element
, UNO_QUERY
);
807 sal_uInt32 nCount
= xContainer
->getCount();
808 Reference
< XInterface
> xIface
;
809 for (sal_uInt32 i
= 0; i
< nCount
; i
++)
811 xContainer
->getByIndex(i
) >>= xIface
;
812 TogglePropertyListening(xIface
);
816 Reference
< XPropertySet
> xSet(Element
, UNO_QUERY
);
820 xSet
->addPropertyChangeListener( OUString(), this );
822 xSet
->removePropertyChangeListener( OUString(), this );
827 void FmXUndoEnvironment::switchListening( const Reference
< XIndexContainer
>& _rxContainer
, bool _bStartListening
)
829 OSL_PRECOND( _rxContainer
.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
830 if ( !_rxContainer
.is() )
835 // if it's an EventAttacherManager, then we need to listen for
837 Reference
< XEventAttacherManager
> xManager( _rxContainer
, UNO_QUERY
);
840 if ( _bStartListening
)
842 m_pScriptingEnv
->registerEventAttacherManager( xManager
);
843 if ( m_vbaListener
.is() )
844 xManager
->addScriptListener( m_vbaListener
);
848 m_pScriptingEnv
->revokeEventAttacherManager( xManager
);
849 if ( m_vbaListener
.is() )
850 xManager
->removeScriptListener( m_vbaListener
);
854 // also handle all children of this element
855 sal_uInt32 nCount
= _rxContainer
->getCount();
856 Reference
< XInterface
> xInterface
;
857 for ( sal_uInt32 i
= 0; i
< nCount
; ++i
)
859 _rxContainer
->getByIndex( i
) >>= xInterface
;
860 if ( _bStartListening
)
861 AddElement( xInterface
);
863 RemoveElement( xInterface
);
866 // be notified of any changes in the container elements
867 Reference
< XContainer
> xSimpleContainer( _rxContainer
, UNO_QUERY
);
868 OSL_ENSURE( xSimpleContainer
.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
869 if ( xSimpleContainer
.is() )
871 if ( _bStartListening
)
872 xSimpleContainer
->addContainerListener( this );
874 xSimpleContainer
->removeContainerListener( this );
877 catch( const Exception
& )
879 TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
884 void FmXUndoEnvironment::switchListening( const Reference
< XInterface
>& _rxObject
, bool _bStartListening
)
886 OSL_PRECOND( _rxObject
.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
892 Reference
< XPropertySet
> xProps( _rxObject
, UNO_QUERY
);
895 if ( _bStartListening
)
896 xProps
->addPropertyChangeListener( OUString(), this );
898 xProps
->removePropertyChangeListener( OUString(), this );
902 Reference
< XModifyBroadcaster
> xBroadcaster( _rxObject
, UNO_QUERY
);
903 if ( xBroadcaster
.is() )
905 if ( _bStartListening
)
906 xBroadcaster
->addModifyListener( this );
908 xBroadcaster
->removeModifyListener( this );
911 catch( const Exception
& )
913 TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
918 void FmXUndoEnvironment::AddElement(const Reference
< XInterface
>& _rxElement
)
920 OSL_ENSURE( !m_bDisposed
, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
922 // listen at the container
923 Reference
< XIndexContainer
> xContainer( _rxElement
, UNO_QUERY
);
924 if ( xContainer
.is() )
925 switchListening( xContainer
, true );
927 switchListening( _rxElement
, true );
931 void FmXUndoEnvironment::RemoveElement(const Reference
< XInterface
>& _rxElement
)
936 switchListening( _rxElement
, false );
940 // reset the ActiveConnection if the form is to be removed. This will (should) free the resources
941 // associated with this connection
942 // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
943 Reference
< XForm
> xForm( _rxElement
, UNO_QUERY
);
944 Reference
< XPropertySet
> xFormProperties( xForm
, UNO_QUERY
);
945 if ( xFormProperties
.is() )
947 Reference
< XConnection
> xDummy
;
948 if ( !isEmbeddedInDatabase( _rxElement
, xDummy
) )
949 // (if there is a connection in the context of the component, setting
950 // a new connection would be vetoed, anyway)
952 xFormProperties
->setPropertyValue( FM_PROP_ACTIVE_CONNECTION
, Any() );
956 Reference
< XIndexContainer
> xContainer( _rxElement
, UNO_QUERY
);
957 if ( xContainer
.is() )
958 switchListening( xContainer
, false );
962 FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel
& rNewMod
, const PropertyChangeEvent
& evt
)
963 :SdrUndoAction(rNewMod
)
964 ,xObj(evt
.Source
, UNO_QUERY
)
965 ,aPropertyName(evt
.PropertyName
)
966 ,aNewValue(evt
.NewValue
)
967 ,aOldValue(evt
.OldValue
)
969 if (rNewMod
.GetObjectShell())
970 rNewMod
.GetObjectShell()->SetModified();
971 if(static_STR_UNDO_PROPERTY
.isEmpty())
972 static_STR_UNDO_PROPERTY
= SvxResId(RID_STR_UNDO_PROPERTY
);
976 void FmUndoPropertyAction::Undo()
978 FmXUndoEnvironment
& rEnv
= static_cast<FmFormModel
&>(rMod
).GetUndoEnv();
980 if (!xObj
.is() || rEnv
.IsLocked())
986 xObj
->setPropertyValue( aPropertyName
, aOldValue
);
988 catch( const Exception
& )
990 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
996 void FmUndoPropertyAction::Redo()
998 FmXUndoEnvironment
& rEnv
= static_cast<FmFormModel
&>(rMod
).GetUndoEnv();
1000 if (!xObj
.is() || rEnv
.IsLocked())
1006 xObj
->setPropertyValue( aPropertyName
, aNewValue
);
1008 catch( const Exception
& )
1010 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
1016 OUString
FmUndoPropertyAction::GetComment() const
1018 OUString aStr
= static_STR_UNDO_PROPERTY
.replaceFirst( "#", aPropertyName
);
1023 FmUndoContainerAction::FmUndoContainerAction(FmFormModel
& _rMod
,
1025 const Reference
< XIndexContainer
> & xCont
,
1026 const Reference
< XInterface
> & xElem
,
1028 :SdrUndoAction( _rMod
)
1029 ,m_xContainer( xCont
)
1031 ,m_eAction( _eAction
)
1033 OSL_ENSURE( nIdx
>= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
1034 // some old code suggested this could be a valid argument. However, this code was
1035 // buggy, and it *seemed* that nobody used it - so it was removed.
1037 if ( !(xCont
.is() && xElem
.is()) )
1042 if ( m_eAction
!= Removed
)
1047 Reference
< XEventAttacherManager
> xManager( xCont
, UNO_QUERY
);
1048 if ( xManager
.is() )
1049 m_aEvents
= xManager
->getScriptEvents(m_nIndex
);
1052 m_xElement
= nullptr;
1054 // we now own the element
1055 m_xOwnElement
= m_xElement
;
1059 FmUndoContainerAction::~FmUndoContainerAction()
1061 // if we own the object...
1062 DisposeElement( m_xOwnElement
);
1066 void FmUndoContainerAction::DisposeElement( const Reference
< XInterface
> & xElem
)
1068 Reference
< XComponent
> xComp( xElem
, UNO_QUERY
);
1071 // and the object does not have a parent
1072 Reference
< XChild
> xChild( xElem
, UNO_QUERY
);
1073 if ( xChild
.is() && !xChild
->getParent().is() )
1080 void FmUndoContainerAction::implReInsert( )
1082 if ( m_xContainer
->getCount() < m_nIndex
)
1085 // insert the element
1087 if ( m_xContainer
->getElementType() == cppu::UnoType
<XFormComponent
>::get() )
1089 aVal
<<= Reference
< XFormComponent
>( m_xElement
, UNO_QUERY
);
1093 aVal
<<= Reference
< XForm
>( m_xElement
, UNO_QUERY
);
1095 m_xContainer
->insertByIndex( m_nIndex
, aVal
);
1097 OSL_ENSURE( getElementPos( m_xContainer
.get(), m_xElement
) == m_nIndex
, "FmUndoContainerAction::implReInsert: insertion did not work!" );
1099 // register the events
1100 Reference
< XEventAttacherManager
> xManager( m_xContainer
, UNO_QUERY
);
1101 if ( xManager
.is() )
1102 xManager
->registerScriptEvents( m_nIndex
, m_aEvents
);
1104 // we don't own the object anymore
1105 m_xOwnElement
= nullptr;
1109 void FmUndoContainerAction::implReRemove( )
1111 Reference
< XInterface
> xElement
;
1112 if ( ( m_nIndex
>= 0 ) && ( m_nIndex
< m_xContainer
->getCount() ) )
1113 m_xContainer
->getByIndex( m_nIndex
) >>= xElement
;
1115 if ( xElement
!= m_xElement
)
1117 // the indexes in the container changed. Okay, so go the long way and
1118 // manually determine the index
1119 m_nIndex
= getElementPos( m_xContainer
.get(), m_xElement
);
1120 if ( m_nIndex
!= -1 )
1121 xElement
= m_xElement
;
1124 OSL_ENSURE( xElement
== m_xElement
, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
1125 if ( xElement
== m_xElement
)
1127 Reference
< XEventAttacherManager
> xManager( m_xContainer
, UNO_QUERY
);
1128 if ( xManager
.is() )
1129 m_aEvents
= xManager
->getScriptEvents( m_nIndex
);
1130 m_xContainer
->removeByIndex( m_nIndex
);
1131 // from now on, we own this object
1132 m_xOwnElement
= m_xElement
;
1137 void FmUndoContainerAction::Undo()
1139 FmXUndoEnvironment
& rEnv
= static_cast< FmFormModel
& >( rMod
).GetUndoEnv();
1141 if ( !(m_xContainer
.is() && !rEnv
.IsLocked() && m_xElement
.is()) )
1147 switch ( m_eAction
)
1158 catch( const Exception
& )
1160 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
1166 void FmUndoContainerAction::Redo()
1168 FmXUndoEnvironment
& rEnv
= static_cast< FmFormModel
& >( rMod
).GetUndoEnv();
1169 if ( !(m_xContainer
.is() && !rEnv
.IsLocked() && m_xElement
.is()) )
1175 switch ( m_eAction
)
1186 catch( const Exception
& )
1188 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
1194 FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel
& _rMod
, SdrUnoObj
* _pObject
, const Reference
< XControlModel
> & _xReplaced
)
1195 :SdrUndoAction(_rMod
)
1196 ,m_xReplaced(_xReplaced
)
1197 ,m_pObject(_pObject
)
1202 FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
1204 // dispose our element if nobody else is responsible for
1205 DisposeElement(m_xReplaced
);
1209 void FmUndoModelReplaceAction::DisposeElement( const css::uno::Reference
< css::awt::XControlModel
>& xReplaced
)
1211 Reference
< XComponent
> xComp(xReplaced
, UNO_QUERY
);
1214 Reference
< XChild
> xChild(xReplaced
, UNO_QUERY
);
1215 if (!xChild
.is() || !xChild
->getParent().is())
1221 void FmUndoModelReplaceAction::Undo()
1225 Reference
< XControlModel
> xCurrentModel( m_pObject
->GetUnoControlModel() );
1227 // replace the model within the parent
1228 Reference
< XChild
> xCurrentAsChild( xCurrentModel
, UNO_QUERY
);
1229 Reference
< XNameContainer
> xCurrentsParent
;
1230 if ( xCurrentAsChild
.is() )
1231 xCurrentsParent
.set(xCurrentAsChild
->getParent(), css::uno::UNO_QUERY
);
1232 DBG_ASSERT( xCurrentsParent
.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
1234 if ( xCurrentsParent
.is() )
1236 // the form container works with FormComponents
1237 Reference
< XFormComponent
> xComponent( m_xReplaced
, UNO_QUERY
);
1238 DBG_ASSERT( xComponent
.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
1240 Reference
< XPropertySet
> xCurrentAsSet( xCurrentModel
, UNO_QUERY
);
1241 DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME
, xCurrentAsSet
), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
1244 xCurrentAsSet
->getPropertyValue( FM_PROP_NAME
) >>= sName
;
1245 xCurrentsParent
->replaceByName( sName
, makeAny( xComponent
) );
1247 m_pObject
->SetUnoControlModel(m_xReplaced
);
1248 m_pObject
->SetChanged();
1250 m_xReplaced
= xCurrentModel
;
1255 OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
1260 OUString
FmUndoModelReplaceAction::GetComment() const
1262 return SvxResId(RID_STR_UNDO_MODEL_REPLACE
);
1265 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */