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>
24 #include <sal/macros.h>
26 #include "fmpgeimp.hxx"
27 #include "svx/svditer.hxx"
30 #include "svx/fmresids.hrc"
31 #include "svx/fmglob.hxx"
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/reflection/XInterfaceMethodTypeDescription.hpp>
44 #include <com/sun/star/sdbc/XConnection.hpp>
46 #include "svx/fmtools.hxx"
47 #include <svl/macitem.hxx>
48 #include <tools/diagnose_ex.h>
49 #include <sfx2/objsh.hxx>
50 #include <sfx2/docfile.hxx>
51 #include <sfx2/app.hxx>
52 #include <sfx2/sfx.hrc>
53 #include <sfx2/event.hxx>
54 #include <osl/mutex.hxx>
55 #include <comphelper/property.hxx>
56 #include <comphelper/uno3.hxx>
57 #include <connectivity/dbtools.hxx>
59 using namespace ::com::sun::star::uno
;
60 using namespace ::com::sun::star::awt
;
61 using namespace ::com::sun::star::beans
;
62 using namespace ::com::sun::star::container
;
63 using namespace ::com::sun::star::script
;
64 using namespace ::com::sun::star::lang
;
65 using namespace ::com::sun::star::form
;
66 using namespace ::com::sun::star::util
;
67 using namespace ::com::sun::star::reflection
;
68 using namespace ::com::sun::star::form::binding
;
69 using namespace ::com::sun::star::sdbc
;
70 using namespace ::svxform
;
71 using namespace ::dbtools
;
74 #include <com/sun/star/script/XScriptListener.hpp>
75 #include <comphelper/processfactory.hxx>
76 #include <cppuhelper/implbase1.hxx>
78 typedef cppu::WeakImplHelper1
< XScriptListener
> ScriptEventListener_BASE
;
79 class ScriptEventListenerWrapper
: public ScriptEventListener_BASE
82 ScriptEventListenerWrapper( FmFormModel
& _rModel
) throw ( RuntimeException
)
84 ,m_attemptedListenerCreation( false )
89 virtual void SAL_CALL
disposing(const EventObject
& ) throw( RuntimeException
, std::exception
) SAL_OVERRIDE
{}
92 virtual void SAL_CALL
firing(const ScriptEvent
& evt
) throw(RuntimeException
, std::exception
) SAL_OVERRIDE
94 attemptListenerCreation();
95 if ( m_vbaListener
.is() )
97 m_vbaListener
->firing( evt
);
101 virtual Any SAL_CALL
approveFiring(const ScriptEvent
& evt
) throw( com::sun::star::reflection::InvocationTargetException
, RuntimeException
, std::exception
) SAL_OVERRIDE
103 attemptListenerCreation();
104 if ( m_vbaListener
.is() )
106 return m_vbaListener
->approveFiring( evt
);
112 void attemptListenerCreation()
114 if ( m_attemptedListenerCreation
)
116 m_attemptedListenerCreation
= true;
120 css::uno::Reference
<css::uno::XComponentContext
> context(
121 comphelper::getProcessComponentContext());
122 Reference
< XScriptListener
> const xScriptListener(
123 context
->getServiceManager()->createInstanceWithContext(
124 "ooo.vba.EventListener", context
),
126 Reference
< XPropertySet
> const xListenerProps( xScriptListener
, UNO_QUERY_THROW
);
127 // SfxObjectShellRef is good here since the model controls the lifetime of the shell
128 SfxObjectShellRef
const xObjectShell
= m_rModel
.GetObjectShell();
129 ENSURE_OR_THROW( xObjectShell
.Is(), "no object shell!" );
130 xListenerProps
->setPropertyValue("Model", makeAny( xObjectShell
->GetModel() ) );
132 m_vbaListener
= xScriptListener
;
134 catch( Exception
const & )
136 DBG_UNHANDLED_EXCEPTION();
139 FmFormModel
& m_rModel
;
140 Reference
< XScriptListener
> m_vbaListener
;
141 bool m_attemptedListenerCreation
;
147 // some helper structs for caching property infos
151 bool bIsTransientOrReadOnly
: 1; // the property is transient or read-only, thus we need no undo action for it
152 bool bIsValueProperty
: 1; // the property is the special value property, thus it may be handled
153 // as if it's transient or persistent
156 struct PropertySetInfo
158 typedef std::map
<OUString
, PropertyInfo
> AllProperties
;
160 AllProperties aProps
; // all properties of this set which we know so far
161 bool bHasEmptyControlSource
; // sal_True -> the set has a DataField property, and the current value is an empty string
162 // sal_False -> the set has _no_ such property or its value isn't empty
165 typedef std::map
<Reference
< XPropertySet
>, PropertySetInfo
> PropertySetInfoCache
;
169 OUString static_STR_UNDO_PROPERTY
;
172 FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel
& _rModel
)
174 ,m_pPropertySetCache( NULL
)
175 ,m_pScriptingEnv( ::svxform::createDefaultFormScriptingEnvironment( _rModel
) )
178 ,m_bDisposed( false )
182 m_vbaListener
= new ScriptEventListenerWrapper( _rModel
);
190 FmXUndoEnvironment::~FmXUndoEnvironment()
192 if ( !m_bDisposed
) // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
193 m_pScriptingEnv
->dispose();
195 if (m_pPropertySetCache
)
196 delete static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
200 void FmXUndoEnvironment::dispose()
202 OSL_ENSURE( !m_bDisposed
, "FmXUndoEnvironment::dispose: disposed twice?" );
208 sal_uInt16 nCount
= rModel
.GetPageCount();
210 for (i
= 0; i
< nCount
; i
++)
212 FmFormPage
* pPage
= PTR_CAST( FmFormPage
, rModel
.GetPage(i
) );
215 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
217 RemoveElement( xForms
);
221 nCount
= rModel
.GetMasterPageCount();
222 for (i
= 0; i
< nCount
; i
++)
224 FmFormPage
* pPage
= PTR_CAST( FmFormPage
, rModel
.GetMasterPage(i
) );
227 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
229 RemoveElement( xForms
);
235 OSL_PRECOND( rModel
.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
236 if ( rModel
.GetObjectShell() )
237 EndListening( *rModel
.GetObjectShell() );
239 if ( IsListening( rModel
) )
240 EndListening( rModel
);
242 m_pScriptingEnv
->dispose();
248 void FmXUndoEnvironment::ModeChanged()
250 OSL_PRECOND( rModel
.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
251 if ( !rModel
.GetObjectShell() )
254 if (bReadOnly
!= (rModel
.GetObjectShell()->IsReadOnly() || rModel
.GetObjectShell()->IsReadOnlyUI()))
256 bReadOnly
= !bReadOnly
;
258 sal_uInt16 nCount
= rModel
.GetPageCount();
260 for (i
= 0; i
< nCount
; i
++)
262 FmFormPage
* pPage
= PTR_CAST( FmFormPage
, rModel
.GetPage(i
) );
265 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
267 TogglePropertyListening( xForms
);
271 nCount
= rModel
.GetMasterPageCount();
272 for (i
= 0; i
< nCount
; i
++)
274 FmFormPage
* pPage
= PTR_CAST( FmFormPage
, rModel
.GetMasterPage(i
) );
277 Reference
< css::form::XForms
> xForms
= pPage
->GetForms( false ).get();
279 TogglePropertyListening( xForms
);
284 StartListening(rModel
);
286 EndListening(rModel
);
291 void FmXUndoEnvironment::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
293 const SdrHint
* pSdrHint
= dynamic_cast<const SdrHint
*>(&rHint
);
296 switch( pSdrHint
->GetKind() )
298 case HINT_OBJINSERTED
:
300 SdrObject
* pSdrObj
= const_cast<SdrObject
*>(pSdrHint
->GetObject());
303 case HINT_OBJREMOVED
:
305 SdrObject
* pSdrObj
= const_cast<SdrObject
*>(pSdrHint
->GetObject());
313 else if (dynamic_cast<const SfxSimpleHint
*>(&rHint
))
315 switch ( static_cast<const SfxSimpleHint
*>(&rHint
)->GetId() )
319 rModel
.SetObjectShell( NULL
);
321 case SFX_HINT_MODECHANGED
:
326 else if (dynamic_cast<const SfxEventHint
*>(&rHint
))
328 switch ( static_cast<const SfxEventHint
*>(&rHint
)->GetEventId() )
330 case SFX_EVENT_CREATEDOC
:
331 case SFX_EVENT_OPENDOC
:
340 void FmXUndoEnvironment::Inserted(SdrObject
* pObj
)
342 if (pObj
->GetObjInventor() == FmFormInventor
)
344 FmFormObj
* pFormObj
= PTR_CAST(FmFormObj
, pObj
);
345 Inserted( pFormObj
);
347 else if (pObj
->IsGroupObject())
349 SdrObjListIter
aIter(*pObj
->GetSubList());
350 while ( aIter
.IsMore() )
351 Inserted( aIter
.Next() );
358 bool lcl_searchElement(const Reference
< XIndexAccess
>& xCont
, const Reference
< XInterface
>& xElement
)
360 if (!xCont
.is() || !xElement
.is())
363 sal_Int32 nCount
= xCont
->getCount();
364 Reference
< XInterface
> xComp
;
365 for (sal_Int32 i
= 0; i
< nCount
; i
++)
369 xCont
->getByIndex(i
) >>= xComp
;
372 if ( xElement
== xComp
)
376 Reference
< XIndexAccess
> xCont2(xComp
, UNO_QUERY
);
377 if (xCont2
.is() && lcl_searchElement(xCont2
, xElement
))
382 catch(const Exception
&)
384 DBG_UNHANDLED_EXCEPTION();
392 void FmXUndoEnvironment::Inserted(FmFormObj
* pObj
)
394 DBG_ASSERT( pObj
, "FmXUndoEnvironment::Inserted: invalid object!" );
398 // ist das Control noch einer Form zugeordnet
399 Reference
< XInterface
> xModel(pObj
->GetUnoControlModel(), UNO_QUERY
);
400 Reference
< XFormComponent
> xContent(xModel
, UNO_QUERY
);
401 if (xContent
.is() && pObj
->GetPage())
403 // if the component doesn't belong to a form, yet, find one to insert into
404 if (!xContent
->getParent().is())
408 Reference
< XIndexContainer
> xObjectParent
= pObj
->GetOriginalParent();
410 FmFormPage
& rPage
= dynamic_cast< FmFormPage
& >( *pObj
->GetPage() );
411 Reference
< XIndexAccess
> xForms( rPage
.GetForms(), UNO_QUERY_THROW
);
413 Reference
< XIndexContainer
> xNewParent
;
414 Reference
< XForm
> xForm
;
416 if ( lcl_searchElement( xForms
, xObjectParent
) )
418 // the form which was the parent of the object when it was removed is still
419 // part of the form component hierarchy of the current page
420 xNewParent
= xObjectParent
;
421 xForm
.set( xNewParent
, UNO_QUERY_THROW
);
422 nPos
= ::std::min( pObj
->GetOriginalIndex(), xNewParent
->getCount() );
426 xForm
.set( rPage
.GetImpl().findPlaceInFormComponentHierarchy( xContent
), UNO_SET_THROW
);
427 xNewParent
.set( xForm
, UNO_QUERY_THROW
);
428 nPos
= xNewParent
->getCount();
431 FmFormPageImpl::setUniqueName( xContent
, xForm
);
432 xNewParent
->insertByIndex( nPos
, makeAny( xContent
) );
434 Reference
< XEventAttacherManager
> xManager( xNewParent
, UNO_QUERY_THROW
);
435 xManager
->registerScriptEvents( nPos
, pObj
->GetOriginalEvents() );
437 catch( const Exception
& )
439 DBG_UNHANDLED_EXCEPTION();
443 // FormObject zuruecksetzen
449 void FmXUndoEnvironment::Removed(SdrObject
* pObj
)
451 if ( pObj
->IsVirtualObj() )
452 // for virtual objects, we've already been notified of the removal of the master
453 // object, which is sufficient here
456 if (pObj
->GetObjInventor() == FmFormInventor
)
458 FmFormObj
* pFormObj
= PTR_CAST(FmFormObj
, pObj
);
461 else if (pObj
->IsGroupObject())
463 SdrObjListIter
aIter(*pObj
->GetSubList());
464 while ( aIter
.IsMore() )
465 Removed( aIter
.Next() );
470 void FmXUndoEnvironment::Removed(FmFormObj
* pObj
)
472 DBG_ASSERT( pObj
, "FmXUndoEnvironment::Removed: invalid object!" );
476 // ist das Control noch einer Form zugeordnet
477 Reference
< XFormComponent
> xContent(pObj
->GetUnoControlModel(), UNO_QUERY
);
480 // das Object wird aus einer Liste herausgenommen
481 // existiert ein Vater wird das Object beim beim Vater entfernt und
482 // am FormObject gemerkt!
484 // wird das Object wieder eingefuegt und ein Parent existiert, so wird dieser
485 // Parent wiederum gesetzt
486 Reference
< XIndexContainer
> xForm(xContent
->getParent(), UNO_QUERY
);
489 Reference
< XIndexAccess
> xIndexAccess((XIndexContainer
*)xForm
.get());
490 // Feststellen an welcher Position sich das Kind befunden hat
491 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();
516 void SAL_CALL
FmXUndoEnvironment::disposing(const EventObject
& e
) throw( RuntimeException
, std::exception
)
518 // check if it's an object we have cached information about
519 if (m_pPropertySetCache
)
521 Reference
< XPropertySet
> xSourceSet(e
.Source
, UNO_QUERY
);
524 PropertySetInfoCache
* pCache
= static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
525 PropertySetInfoCache::iterator aSetPos
= pCache
->find(xSourceSet
);
526 if (aSetPos
!= pCache
->end())
527 pCache
->erase(aSetPos
);
532 // XPropertyChangeListener
534 void SAL_CALL
FmXUndoEnvironment::propertyChange(const PropertyChangeEvent
& evt
) throw(::com::sun::star::uno::RuntimeException
, std::exception
)
536 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
540 Reference
< XPropertySet
> xSet(evt
.Source
, UNO_QUERY
);
544 // if it's a "default value" property of a control model, set the according "value" property
545 static const OUString pDefaultValueProperties
[] = {
546 OUString(FM_PROP_DEFAULT_TEXT
), OUString(FM_PROP_DEFAULTCHECKED
), OUString(FM_PROP_DEFAULT_DATE
), OUString(FM_PROP_DEFAULT_TIME
),
547 OUString(FM_PROP_DEFAULT_VALUE
), OUString(FM_PROP_DEFAULT_SELECT_SEQ
), OUString(FM_PROP_EFFECTIVE_DEFAULT
)
549 const OUString aValueProperties
[] = {
550 OUString(FM_PROP_TEXT
), OUString(FM_PROP_STATE
), OUString(FM_PROP_DATE
), OUString(FM_PROP_TIME
),
551 OUString(FM_PROP_VALUE
), OUString(FM_PROP_SELECT_SEQ
), OUString(FM_PROP_EFFECTIVE_VALUE
)
553 sal_Int32 nDefaultValueProps
= sizeof(pDefaultValueProperties
)/sizeof(pDefaultValueProperties
[0]);
554 OSL_ENSURE(sizeof(aValueProperties
)/sizeof(aValueProperties
[0]) == nDefaultValueProps
,
555 "FmXUndoEnvironment::propertyChange: inconsistence!");
556 for (sal_Int32 i
=0; i
<nDefaultValueProps
; ++i
)
558 if (evt
.PropertyName
== pDefaultValueProperties
[i
])
562 xSet
->setPropertyValue(aValueProperties
[i
], evt
.NewValue
);
564 catch(const Exception
&)
566 OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
571 // no Undo for transient and readonly props. But unfortunately "transient" is not only that the
572 // "transient" flag is set for the property in question, instead it is somewhat more complex
573 // Transience criterions are:
574 // - the "transient" flag is set for the property
575 // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
576 // to a database column. Note that it doesn't matter here whether the control actually
577 // *is* bound to a column
578 // - OR the control is bound to an external value via XBindableValue/XValueBinding
579 // which does not have a "ExternalData" property being <TRUE/>
581 if (!m_pPropertySetCache
)
582 m_pPropertySetCache
= new PropertySetInfoCache
;
583 PropertySetInfoCache
* pCache
= static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
585 // let's see if we know something about the set
586 PropertySetInfoCache::iterator aSetPos
= pCache
->find(xSet
);
587 if (aSetPos
== pCache
->end())
589 PropertySetInfo aNewEntry
;
590 if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE
, xSet
))
592 aNewEntry
.bHasEmptyControlSource
= false;
598 Any aCurrentControlSource
= xSet
->getPropertyValue(FM_PROP_CONTROLSOURCE
);
599 aNewEntry
.bHasEmptyControlSource
= !aCurrentControlSource
.hasValue() || ::comphelper::getString(aCurrentControlSource
).isEmpty();
601 catch(const Exception
&)
603 DBG_UNHANDLED_EXCEPTION();
606 aSetPos
= pCache
->insert(PropertySetInfoCache::value_type(xSet
,aNewEntry
)).first
;
607 DBG_ASSERT(aSetPos
!= pCache
->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
610 { // is it the DataField property ?
611 if (evt
.PropertyName
== FM_PROP_CONTROLSOURCE
)
613 aSetPos
->second
.bHasEmptyControlSource
= !evt
.NewValue
.hasValue() || ::comphelper::getString(evt
.NewValue
).isEmpty();
617 // now we have access to the cached info about the set
618 // let's see what we know about the property
619 PropertySetInfo::AllProperties
& rPropInfos
= aSetPos
->second
.aProps
;
620 PropertySetInfo::AllProperties::iterator aPropertyPos
= rPropInfos
.find(evt
.PropertyName
);
621 if (aPropertyPos
== rPropInfos
.end())
622 { // nothing 'til now ... have to change this ....
623 PropertyInfo aNewEntry
;
626 sal_Int32 nAttributes
= xSet
->getPropertySetInfo()->getPropertyByName(evt
.PropertyName
).Attributes
;
627 aNewEntry
.bIsTransientOrReadOnly
= ((nAttributes
& PropertyAttribute::READONLY
) != 0) || ((nAttributes
& PropertyAttribute::TRANSIENT
) != 0);
629 // check if it is the special "DataFieldProperty"
630 aNewEntry
.bIsValueProperty
= false;
633 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY
, xSet
))
635 Any aControlSourceProperty
= xSet
->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY
);
636 OUString sControlSourceProperty
;
637 aControlSourceProperty
>>= sControlSourceProperty
;
639 aNewEntry
.bIsValueProperty
= (sControlSourceProperty
.equals(evt
.PropertyName
));
642 catch(const Exception
&)
644 DBG_UNHANDLED_EXCEPTION();
647 // insert the new entry
648 aPropertyPos
= rPropInfos
.insert(PropertySetInfo::AllProperties::value_type(evt
.PropertyName
,aNewEntry
)).first
;
649 DBG_ASSERT(aPropertyPos
!= rPropInfos
.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
652 // now we have access to the cached info about the property affected
653 // and are able to decide whether or not we need an undo action
655 bool bAddUndoAction
= rModel
.IsUndoEnabled();
656 // no UNDO for transient/readonly properties
657 if ( bAddUndoAction
&& aPropertyPos
->second
.bIsTransientOrReadOnly
)
658 bAddUndoAction
= false;
660 if ( bAddUndoAction
&& aPropertyPos
->second
.bIsValueProperty
)
662 // no UNDO when the "value" property changes, but the ControlSource is non-empty
663 // (in this case the control is intended to be bound to a database column)
664 if ( !aSetPos
->second
.bHasEmptyControlSource
)
665 bAddUndoAction
= false;
667 // no UNDO if the control is currently bound to an external value
668 if ( bAddUndoAction
)
670 Reference
< XBindableValue
> xBindable( evt
.Source
, UNO_QUERY
);
671 Reference
< XValueBinding
> xBinding
;
672 if ( xBindable
.is() )
673 xBinding
= xBindable
->getValueBinding();
675 Reference
< XPropertySet
> xBindingProps
;
676 Reference
< XPropertySetInfo
> xBindingPropsPSI
;
677 if ( xBindable
.is() )
678 xBindingProps
.set( xBinding
, UNO_QUERY
);
679 if ( xBindingProps
.is() )
680 xBindingPropsPSI
= xBindingProps
->getPropertySetInfo();
681 // TODO: we should cache all those things, else this might be too expensive.
682 // However, this requires we're notified of changes in the value binding
684 static const char s_sExternalData
[] = "ExternalData";
685 if ( xBindingPropsPSI
.is() && xBindingPropsPSI
->hasPropertyByName( s_sExternalData
) )
687 bool bExternalData
= true;
688 OSL_VERIFY( xBindingProps
->getPropertyValue( s_sExternalData
) >>= bExternalData
);
689 bAddUndoAction
= !bExternalData
;
692 bAddUndoAction
= !xBinding
.is();
696 if ( bAddUndoAction
&& ( evt
.PropertyName
== FM_PROP_STRINGITEMLIST
) )
698 Reference
< XListEntrySink
> xSink( evt
.Source
, UNO_QUERY
);
699 if ( xSink
.is() && xSink
->getListEntrySource().is() )
700 // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
701 bAddUndoAction
= false;
704 if ( bAddUndoAction
)
707 // TODO: this is a potential race condition: two threads here could in theory
708 // add their undo actions out-of-order
710 SolarMutexGuard aSolarGuard
;
711 rModel
.AddUndo(new FmUndoPropertyAction(rModel
, evt
));
716 // if it's the DataField property we may have to adjust our cache
717 if (m_pPropertySetCache
&& evt
.PropertyName
== FM_PROP_CONTROLSOURCE
)
719 Reference
< XPropertySet
> xSet(evt
.Source
, UNO_QUERY
);
720 PropertySetInfoCache
* pCache
= static_cast<PropertySetInfoCache
*>(m_pPropertySetCache
);
721 PropertySetInfo
& rSetInfo
= (*pCache
)[xSet
];
722 rSetInfo
.bHasEmptyControlSource
= !evt
.NewValue
.hasValue() || ::comphelper::getString(evt
.NewValue
).isEmpty();
727 // XContainerListener
729 void SAL_CALL
FmXUndoEnvironment::elementInserted(const ContainerEvent
& evt
) throw(::com::sun::star::uno::RuntimeException
, std::exception
)
731 SolarMutexGuard aSolarGuard
;
732 ::osl::MutexGuard
aGuard( m_aMutex
);
734 // neues Object zum lauschen
735 Reference
< XInterface
> xIface
;
736 evt
.Element
>>= xIface
;
737 OSL_ENSURE(xIface
.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
744 void FmXUndoEnvironment::implSetModified()
746 if ( !IsLocked() && rModel
.GetObjectShell() )
748 rModel
.GetObjectShell()->SetModified( true );
753 void SAL_CALL
FmXUndoEnvironment::elementReplaced(const ContainerEvent
& evt
) throw(::com::sun::star::uno::RuntimeException
, std::exception
)
755 SolarMutexGuard aSolarGuard
;
756 ::osl::MutexGuard
aGuard( m_aMutex
);
758 Reference
< XInterface
> xIface
;
759 evt
.ReplacedElement
>>= xIface
;
760 OSL_ENSURE(xIface
.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
761 RemoveElement(xIface
);
763 evt
.Element
>>= xIface
;
770 void SAL_CALL
FmXUndoEnvironment::elementRemoved(const ContainerEvent
& evt
) throw(::com::sun::star::uno::RuntimeException
, std::exception
)
772 SolarMutexGuard aSolarGuard
;
773 ::osl::MutexGuard
aGuard( m_aMutex
);
775 Reference
< XInterface
> xIface( evt
.Element
, UNO_QUERY
);
776 OSL_ENSURE(xIface
.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
777 RemoveElement(xIface
);
783 void SAL_CALL
FmXUndoEnvironment::modified( const EventObject
& /*aEvent*/ ) throw (RuntimeException
, std::exception
)
789 void FmXUndoEnvironment::AddForms(const Reference
< XNameContainer
> & rForms
)
792 AddElement(Reference
<XInterface
>( rForms
, UNO_QUERY
));
797 void FmXUndoEnvironment::RemoveForms(const Reference
< XNameContainer
> & rForms
)
800 RemoveElement(Reference
<XInterface
>( rForms
, UNO_QUERY
));
805 void FmXUndoEnvironment::TogglePropertyListening(const Reference
< XInterface
> & Element
)
807 // am Container horchen
808 Reference
< XIndexContainer
> xContainer(Element
, UNO_QUERY
);
811 sal_uInt32 nCount
= xContainer
->getCount();
812 Reference
< XInterface
> xIface
;
813 for (sal_uInt32 i
= 0; i
< nCount
; i
++)
815 xContainer
->getByIndex(i
) >>= xIface
;
816 TogglePropertyListening(xIface
);
820 Reference
< XPropertySet
> xSet(Element
, UNO_QUERY
);
824 xSet
->addPropertyChangeListener( OUString(), this );
826 xSet
->removePropertyChangeListener( OUString(), this );
832 void FmXUndoEnvironment::switchListening( const Reference
< XIndexContainer
>& _rxContainer
, bool _bStartListening
)
834 OSL_PRECOND( _rxContainer
.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
835 if ( !_rxContainer
.is() )
840 // if it's an EventAttacherManager, then we need to listen for
842 Reference
< XEventAttacherManager
> xManager( _rxContainer
, UNO_QUERY
);
845 if ( _bStartListening
)
847 m_pScriptingEnv
->registerEventAttacherManager( xManager
);
848 if ( m_vbaListener
.is() )
849 xManager
->addScriptListener( m_vbaListener
);
853 m_pScriptingEnv
->revokeEventAttacherManager( xManager
);
854 if ( m_vbaListener
.is() )
855 xManager
->removeScriptListener( m_vbaListener
);
859 // also handle all children of this element
860 sal_uInt32 nCount
= _rxContainer
->getCount();
861 Reference
< XInterface
> xInterface
;
862 for ( sal_uInt32 i
= 0; i
< nCount
; ++i
)
864 _rxContainer
->getByIndex( i
) >>= xInterface
;
865 if ( _bStartListening
)
866 AddElement( xInterface
);
868 RemoveElement( xInterface
);
871 // be notified of any changes in the container elements
872 Reference
< XContainer
> xSimpleContainer( _rxContainer
, UNO_QUERY
);
873 OSL_ENSURE( xSimpleContainer
.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
874 if ( xSimpleContainer
.is() )
876 if ( _bStartListening
)
877 xSimpleContainer
->addContainerListener( this );
879 xSimpleContainer
->removeContainerListener( this );
882 catch( const Exception
& )
884 OSL_FAIL( "FmXUndoEnvironment::switchListening: caught an exception!" );
889 void FmXUndoEnvironment::switchListening( const Reference
< XInterface
>& _rxObject
, bool _bStartListening
)
891 OSL_PRECOND( _rxObject
.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
897 Reference
< XPropertySet
> xProps( _rxObject
, UNO_QUERY
);
900 if ( _bStartListening
)
901 xProps
->addPropertyChangeListener( OUString(), this );
903 xProps
->removePropertyChangeListener( OUString(), this );
907 Reference
< XModifyBroadcaster
> xBroadcaster( _rxObject
, UNO_QUERY
);
908 if ( xBroadcaster
.is() )
910 if ( _bStartListening
)
911 xBroadcaster
->addModifyListener( this );
913 xBroadcaster
->removeModifyListener( this );
916 catch( const Exception
& )
918 OSL_FAIL( "FmXUndoEnvironment::switchListening: caught an exception!" );
923 void FmXUndoEnvironment::AddElement(const Reference
< XInterface
>& _rxElement
)
925 OSL_ENSURE( !m_bDisposed
, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
927 // am Container horchen
928 Reference
< XIndexContainer
> xContainer( _rxElement
, UNO_QUERY
);
929 if ( xContainer
.is() )
930 switchListening( xContainer
, true );
932 switchListening( _rxElement
, true );
936 void FmXUndoEnvironment::RemoveElement(const Reference
< XInterface
>& _rxElement
)
941 switchListening( _rxElement
, false );
945 // reset the ActiveConnection if the form is to be removed. This will (should) free the resources
946 // associated with this connection
947 // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
948 Reference
< XForm
> xForm( _rxElement
, UNO_QUERY
);
949 Reference
< XPropertySet
> xFormProperties( xForm
, UNO_QUERY
);
950 if ( xFormProperties
.is() )
952 Reference
< XConnection
> xDummy
;
953 if ( !isEmbeddedInDatabase( _rxElement
, xDummy
) )
954 // (if there is a connection in the context of the component, setting
955 // a new connection would be vetoed, anyway)
957 xFormProperties
->setPropertyValue( FM_PROP_ACTIVE_CONNECTION
, Any() );
961 Reference
< XIndexContainer
> xContainer( _rxElement
, UNO_QUERY
);
962 if ( xContainer
.is() )
963 switchListening( xContainer
, false );
968 FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel
& rNewMod
, const PropertyChangeEvent
& evt
)
969 :SdrUndoAction(rNewMod
)
970 ,xObj(evt
.Source
, UNO_QUERY
)
971 ,aPropertyName(evt
.PropertyName
)
972 ,aNewValue(evt
.NewValue
)
973 ,aOldValue(evt
.OldValue
)
975 if (rNewMod
.GetObjectShell())
976 rNewMod
.GetObjectShell()->SetModified(true);
977 if(static_STR_UNDO_PROPERTY
.isEmpty())
978 static_STR_UNDO_PROPERTY
= SVX_RESSTR(RID_STR_UNDO_PROPERTY
);
983 void FmUndoPropertyAction::Undo()
985 FmXUndoEnvironment
& rEnv
= static_cast<FmFormModel
&>(rMod
).GetUndoEnv();
987 if (xObj
.is() && !rEnv
.IsLocked())
992 xObj
->setPropertyValue( aPropertyName
, aOldValue
);
994 catch( const Exception
& )
996 OSL_FAIL( "FmUndoPropertyAction::Undo: caught an exception!" );
1003 void FmUndoPropertyAction::Redo()
1005 FmXUndoEnvironment
& rEnv
= static_cast<FmFormModel
&>(rMod
).GetUndoEnv();
1007 if (xObj
.is() && !rEnv
.IsLocked())
1012 xObj
->setPropertyValue( aPropertyName
, aNewValue
);
1014 catch( const Exception
& )
1016 OSL_FAIL( "FmUndoPropertyAction::Redo: caught an exception!" );
1023 OUString
FmUndoPropertyAction::GetComment() const
1025 OUString
aStr(static_STR_UNDO_PROPERTY
);
1027 aStr
= aStr
.replaceFirst( "#", aPropertyName
);
1033 FmUndoContainerAction::FmUndoContainerAction(FmFormModel
& _rMod
,
1035 const Reference
< XIndexContainer
> & xCont
,
1036 const Reference
< XInterface
> & xElem
,
1038 :SdrUndoAction( _rMod
)
1039 ,m_xContainer( xCont
)
1041 ,m_eAction( _eAction
)
1043 OSL_ENSURE( nIdx
>= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
1044 // some old code suggested this could be a valid argument. However, this code was
1045 // buggy, and it *seemed* that nobody used it - so it was removed.
1047 if ( xCont
.is() && xElem
.is() )
1050 m_xElement
.set(xElem
, css::uno::UNO_QUERY
);
1051 if ( m_eAction
== Removed
)
1055 Reference
< XEventAttacherManager
> xManager( xCont
, UNO_QUERY
);
1056 if ( xManager
.is() )
1057 m_aEvents
= xManager
->getScriptEvents(m_nIndex
);
1062 // we now own the element
1063 m_xOwnElement
= m_xElement
;
1069 FmUndoContainerAction::~FmUndoContainerAction()
1071 // if we own the object ....
1072 DisposeElement( m_xOwnElement
);
1077 void FmUndoContainerAction::DisposeElement( const Reference
< XInterface
> & xElem
)
1079 Reference
< XComponent
> xComp( xElem
, UNO_QUERY
);
1082 // and the object does not have a parent
1083 Reference
< XChild
> xChild( xElem
, UNO_QUERY
);
1084 if ( xChild
.is() && !xChild
->getParent().is() )
1091 void FmUndoContainerAction::implReInsert( )
1093 if ( m_xContainer
->getCount() >= m_nIndex
)
1095 // insert the element
1097 if ( m_xContainer
->getElementType() == cppu::UnoType
<XFormComponent
>::get() )
1099 aVal
<<= Reference
< XFormComponent
>( m_xElement
, UNO_QUERY
);
1103 aVal
<<= Reference
< XForm
>( m_xElement
, UNO_QUERY
);
1105 m_xContainer
->insertByIndex( m_nIndex
, aVal
);
1107 OSL_ENSURE( getElementPos( m_xContainer
.get(), m_xElement
) == m_nIndex
, "FmUndoContainerAction::implReInsert: insertion did not work!" );
1109 // register the events
1110 Reference
< XEventAttacherManager
> xManager( m_xContainer
, UNO_QUERY
);
1111 if ( xManager
.is() )
1112 xManager
->registerScriptEvents( m_nIndex
, m_aEvents
);
1114 // we don't own the object anymore
1115 m_xOwnElement
= NULL
;
1120 void FmUndoContainerAction::implReRemove( )
1122 Reference
< XInterface
> xElement
;
1123 if ( ( m_nIndex
>= 0 ) && ( m_nIndex
< m_xContainer
->getCount() ) )
1124 m_xContainer
->getByIndex( m_nIndex
) >>= xElement
;
1126 if ( xElement
!= m_xElement
)
1128 // the indexes in the container changed. Okay, so go the long way and
1129 // manually determine the index
1130 m_nIndex
= getElementPos( m_xContainer
.get(), m_xElement
);
1131 if ( m_nIndex
!= -1 )
1132 xElement
= m_xElement
;
1135 OSL_ENSURE( xElement
== m_xElement
, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
1136 if ( xElement
== m_xElement
)
1138 Reference
< XEventAttacherManager
> xManager( m_xContainer
, UNO_QUERY
);
1139 if ( xManager
.is() )
1140 m_aEvents
= xManager
->getScriptEvents( m_nIndex
);
1141 m_xContainer
->removeByIndex( m_nIndex
);
1142 // from now on, we own this object
1143 m_xOwnElement
= m_xElement
;
1148 void FmUndoContainerAction::Undo()
1150 FmXUndoEnvironment
& rEnv
= static_cast< FmFormModel
& >( rMod
).GetUndoEnv();
1152 if ( m_xContainer
.is() && !rEnv
.IsLocked() && m_xElement
.is() )
1157 switch ( m_eAction
)
1168 catch( const Exception
& )
1170 OSL_FAIL( "FmUndoContainerAction::Undo: caught an exception!" );
1177 void FmUndoContainerAction::Redo()
1179 FmXUndoEnvironment
& rEnv
= static_cast< FmFormModel
& >( rMod
).GetUndoEnv();
1180 if ( m_xContainer
.is() && !rEnv
.IsLocked() && m_xElement
.is() )
1185 switch ( m_eAction
)
1196 catch( const Exception
& )
1198 OSL_FAIL( "FmUndoContainerAction::Redo: caught an exception!" );
1205 FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel
& _rMod
, SdrUnoObj
* _pObject
, const Reference
< XControlModel
> & _xReplaced
)
1206 :SdrUndoAction(_rMod
)
1207 ,m_xReplaced(_xReplaced
)
1208 ,m_pObject(_pObject
)
1213 FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
1215 // dispose our element if nobody else is responsible for
1216 DisposeElement(m_xReplaced
);
1221 void FmUndoModelReplaceAction::DisposeElement( const ::com::sun::star::uno::Reference
< ::com::sun::star::awt::XControlModel
>& xReplaced
)
1223 Reference
< XComponent
> xComp(xReplaced
, UNO_QUERY
);
1226 Reference
< XChild
> xChild(xReplaced
, UNO_QUERY
);
1227 if (!xChild
.is() || !xChild
->getParent().is())
1233 void FmUndoModelReplaceAction::Undo()
1237 Reference
< XControlModel
> xCurrentModel( m_pObject
->GetUnoControlModel() );
1239 // replace the model within the parent
1240 Reference
< XChild
> xCurrentAsChild( xCurrentModel
, UNO_QUERY
);
1241 Reference
< XNameContainer
> xCurrentsParent
;
1242 if ( xCurrentAsChild
.is() )
1243 xCurrentsParent
.set(xCurrentAsChild
->getParent(), css::uno::UNO_QUERY
);
1244 DBG_ASSERT( xCurrentsParent
.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
1246 if ( xCurrentsParent
.is() )
1248 // the form container works with FormComponents
1249 Reference
< XFormComponent
> xComponent( m_xReplaced
, UNO_QUERY
);
1250 DBG_ASSERT( xComponent
.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
1252 Reference
< XPropertySet
> xCurrentAsSet( xCurrentModel
, UNO_QUERY
);
1253 DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME
, xCurrentAsSet
), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
1256 xCurrentAsSet
->getPropertyValue( FM_PROP_NAME
) >>= sName
;
1257 xCurrentsParent
->replaceByName( sName
, makeAny( xComponent
) );
1259 m_pObject
->SetUnoControlModel(m_xReplaced
);
1260 m_pObject
->SetChanged();
1262 m_xReplaced
= xCurrentModel
;
1267 OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
1272 OUString
FmUndoModelReplaceAction::GetComment() const
1274 return SVX_RESSTR(RID_STR_UNDO_MODEL_REPLACE
);
1277 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */