Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / form / fmundo.cxx
blob9f627a7358b4cfe126268c3eca702a25141d902b
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 .
20 #include <sal/config.h>
22 #include <map>
24 #include <sal/macros.h>
25 #include <fmundo.hxx>
26 #include <fmpgeimp.hxx>
27 #include <svx/svditer.hxx>
28 #include <fmobj.hxx>
29 #include <fmprop.hxx>
30 #include <svx/strings.hrc>
31 #include <svx/dialmgr.hxx>
32 #include <svx/fmmodel.hxx>
33 #include <svx/fmpage.hxx>
35 #include <com/sun/star/util/XModifyBroadcaster.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
37 #include <com/sun/star/container/XContainer.hpp>
38 #include <com/sun/star/container/XContainerListener.hpp>
39 #include <com/sun/star/script/XEventAttacherManager.hpp>
40 #include <com/sun/star/form/binding/XBindableValue.hpp>
41 #include <com/sun/star/form/binding/XListEntrySink.hpp>
42 #include <com/sun/star/sdbc/XConnection.hpp>
43 #include <com/sun/star/uno/XComponentContext.hpp>
45 #include <svx/fmtools.hxx>
46 #include <tools/debug.hxx>
47 #include <comphelper/diagnose_ex.hxx>
48 #include <sfx2/objsh.hxx>
49 #include <sfx2/event.hxx>
50 #include <osl/mutex.hxx>
51 #include <comphelper/property.hxx>
52 #include <comphelper/types.hxx>
53 #include <connectivity/dbtools.hxx>
54 #include <vcl/svapp.hxx>
56 using namespace ::com::sun::star::uno;
57 using namespace ::com::sun::star::awt;
58 using namespace ::com::sun::star::beans;
59 using namespace ::com::sun::star::container;
60 using namespace ::com::sun::star::script;
61 using namespace ::com::sun::star::lang;
62 using namespace ::com::sun::star::form;
63 using namespace ::com::sun::star::util;
64 using namespace ::com::sun::star::form::binding;
65 using namespace ::com::sun::star::sdbc;
66 using namespace ::svxform;
67 using namespace ::dbtools;
70 #include <com/sun/star/script/XScriptListener.hpp>
71 #include <comphelper/processfactory.hxx>
72 #include <cppuhelper/implbase.hxx>
74 namespace {
76 class ScriptEventListenerWrapper : public cppu::WeakImplHelper< XScriptListener >
78 public:
79 /// @throws css::uno::RuntimeException
80 explicit ScriptEventListenerWrapper( FmFormModel& _rModel)
81 :m_rModel( _rModel )
82 ,m_attemptedListenerCreation( false )
86 // XEventListener
87 virtual void SAL_CALL disposing(const EventObject& ) override {}
89 // XScriptListener
90 virtual void SAL_CALL firing(const ScriptEvent& evt) override
92 attemptListenerCreation();
93 if ( m_vbaListener.is() )
95 m_vbaListener->firing( evt );
99 virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override
101 attemptListenerCreation();
102 if ( m_vbaListener.is() )
104 return m_vbaListener->approveFiring( evt );
106 return Any();
109 private:
110 void attemptListenerCreation()
112 if ( m_attemptedListenerCreation )
113 return;
114 m_attemptedListenerCreation = true;
118 css::uno::Reference<css::uno::XComponentContext> context(
119 comphelper::getProcessComponentContext());
120 Reference< XScriptListener > const xScriptListener(
121 context->getServiceManager()->createInstanceWithContext(
122 "ooo.vba.EventListener", context),
123 UNO_QUERY_THROW);
124 Reference< XPropertySet > const xListenerProps( xScriptListener, UNO_QUERY_THROW );
125 // SfxObjectShellRef is good here since the model controls the lifetime of the shell
126 SfxObjectShellRef const xObjectShell = m_rModel.GetObjectShell();
127 ENSURE_OR_THROW( xObjectShell.is(), "no object shell!" );
128 xListenerProps->setPropertyValue("Model", Any( xObjectShell->GetModel() ) );
130 m_vbaListener = xScriptListener;
132 catch( Exception const & )
134 DBG_UNHANDLED_EXCEPTION("svx");
137 FmFormModel& m_rModel;
138 Reference< XScriptListener > m_vbaListener;
139 bool m_attemptedListenerCreation;
145 // some helper structs for caching property infos
147 struct PropertyInfo
149 bool bIsTransientOrReadOnly : 1; // the property is transient or read-only, thus we need no undo action for it
150 bool bIsValueProperty : 1; // the property is the special value property, thus it may be handled
151 // as if it's transient or persistent
154 struct PropertySetInfo
156 typedef std::map<OUString, PropertyInfo> AllProperties;
158 AllProperties aProps; // all properties of this set which we know so far
159 bool bHasEmptyControlSource; // sal_True -> the set has a DataField property, and the current value is an empty string
160 // sal_False -> the set has _no_ such property or its value isn't empty
165 typedef std::map<Reference< XPropertySet >, PropertySetInfo> PropertySetInfoCache;
168 static OUString static_STR_UNDO_PROPERTY;
171 FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel& _rModel)
172 :rModel( _rModel )
173 ,m_pPropertySetCache( nullptr )
174 ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel ) )
175 ,m_Locks( 0 )
176 ,bReadOnly( false )
177 ,m_bDisposed( false )
181 m_vbaListener = new ScriptEventListenerWrapper( _rModel );
183 catch( Exception& )
188 FmXUndoEnvironment::~FmXUndoEnvironment()
190 if ( !m_bDisposed ) // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
191 m_pScriptingEnv->dispose();
193 if (m_pPropertySetCache)
194 delete static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
197 void FmXUndoEnvironment::dispose()
199 OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::dispose: disposed twice?" );
200 if ( !m_bDisposed )
201 return;
203 Lock();
205 sal_uInt16 nCount = rModel.GetPageCount();
206 sal_uInt16 i;
207 for (i = 0; i < nCount; i++)
209 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
210 if ( pPage )
212 Reference< css::form::XForms > xForms = pPage->GetForms( false );
213 if ( xForms.is() )
214 RemoveElement( xForms );
218 nCount = rModel.GetMasterPageCount();
219 for (i = 0; i < nCount; i++)
221 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
222 if ( pPage )
224 Reference< css::form::XForms > xForms = pPage->GetForms( false );
225 if ( xForms.is() )
226 RemoveElement( xForms );
230 UnLock();
232 OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
233 if ( rModel.GetObjectShell() )
234 EndListening( *rModel.GetObjectShell() );
236 if ( IsListening( rModel ) )
237 EndListening( rModel );
239 m_pScriptingEnv->dispose();
241 m_bDisposed = true;
245 void FmXUndoEnvironment::ModeChanged()
247 OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
248 if ( !rModel.GetObjectShell() )
249 return;
251 if (bReadOnly == (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
252 return;
254 bReadOnly = !bReadOnly;
256 sal_uInt16 nCount = rModel.GetPageCount();
257 sal_uInt16 i;
258 for (i = 0; i < nCount; i++)
260 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
261 if ( pPage )
263 Reference< css::form::XForms > xForms = pPage->GetForms( false );
264 if ( xForms.is() )
265 TogglePropertyListening( xForms );
269 nCount = rModel.GetMasterPageCount();
270 for (i = 0; i < nCount; i++)
272 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
273 if ( pPage )
275 Reference< css::form::XForms > xForms = pPage->GetForms( false );
276 if ( xForms.is() )
277 TogglePropertyListening( xForms );
281 if (!bReadOnly)
282 StartListening(rModel);
283 else
284 EndListening(rModel);
288 void FmXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
290 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
292 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
293 switch (pSdrHint->GetKind())
295 case SdrHintKind::ObjectInserted:
297 SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
298 Inserted( pSdrObj );
299 } break;
300 case SdrHintKind::ObjectRemoved:
302 SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
303 Removed( pSdrObj );
305 break;
306 default:
307 break;
310 else if (rHint.GetId() != SfxHintId::NONE)
312 switch (rHint.GetId())
314 case SfxHintId::Dying:
315 dispose();
316 rModel.SetObjectShell( nullptr );
317 break;
318 case SfxHintId::ModeChanged:
319 ModeChanged();
320 break;
321 default: break;
324 else if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint))
326 switch (pEventHint->GetEventId())
328 case SfxEventHintId::CreateDoc:
329 case SfxEventHintId::OpenDoc:
330 ModeChanged();
331 break;
332 default: break;
337 void FmXUndoEnvironment::Inserted(SdrObject* pObj)
339 if (pObj->GetObjInventor() == SdrInventor::FmForm)
341 FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
342 Inserted( pFormObj );
344 else if (pObj->IsGroupObject())
346 SdrObjListIter aIter(pObj->GetSubList());
347 while ( aIter.IsMore() )
348 Inserted( aIter.Next() );
353 namespace
355 bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
357 if (!xCont.is() || !xElement.is())
358 return false;
360 sal_Int32 nCount = xCont->getCount();
361 Reference< XInterface > xComp;
362 for (sal_Int32 i = 0; i < nCount; i++)
366 xCont->getByIndex(i) >>= xComp;
367 if (xComp.is())
369 if ( xElement == xComp )
370 return true;
371 else
373 Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
374 if (xCont2.is() && lcl_searchElement(xCont2, xElement))
375 return true;
379 catch(const Exception&)
381 DBG_UNHANDLED_EXCEPTION("svx");
384 return false;
389 void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
391 DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
392 if ( !pObj )
393 return;
395 // is the control still assigned to a form
396 Reference< XInterface > xModel(pObj->GetUnoControlModel(), UNO_QUERY);
397 Reference< XFormComponent > xContent(xModel, UNO_QUERY);
398 if (!(xContent.is() && pObj->getSdrPageFromSdrObject()))
399 return;
401 // if the component doesn't belong to a form, yet, find one to insert into
402 if (!xContent->getParent().is())
406 const Reference< XIndexContainer >& xObjectParent = pObj->GetOriginalParent();
408 FmFormPage& rPage(dynamic_cast< FmFormPage& >( *pObj->getSdrPageFromSdrObject()));
409 Reference< XIndexAccess > xForms( rPage.GetForms(), UNO_QUERY_THROW );
411 Reference< XIndexContainer > xNewParent;
412 Reference< XForm > xForm;
413 sal_Int32 nPos = -1;
414 if ( lcl_searchElement( xForms, xObjectParent ) )
416 // the form which was the parent of the object when it was removed is still
417 // part of the form component hierarchy of the current page
418 xNewParent = xObjectParent;
419 xForm.set( xNewParent, UNO_QUERY_THROW );
420 nPos = ::std::min( pObj->GetOriginalIndex(), xNewParent->getCount() );
422 else
424 xForm.set( rPage.GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
425 xNewParent.set( xForm, UNO_QUERY_THROW );
426 nPos = xNewParent->getCount();
429 FmFormPageImpl::setUniqueName( xContent, xForm );
430 xNewParent->insertByIndex( nPos, Any( xContent ) );
432 Reference< XEventAttacherManager > xManager( xNewParent, UNO_QUERY_THROW );
433 xManager->registerScriptEvents( nPos, pObj->GetOriginalEvents() );
435 catch( const Exception& )
437 DBG_UNHANDLED_EXCEPTION("svx");
441 // reset FormObject
442 pObj->ClearObjEnv();
446 void FmXUndoEnvironment::Removed(SdrObject* pObj)
448 if ( pObj->IsVirtualObj() )
449 // for virtual objects, we've already been notified of the removal of the master
450 // object, which is sufficient here
451 return;
453 if (pObj->GetObjInventor() == SdrInventor::FmForm)
455 FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
456 Removed(pFormObj);
458 else if (pObj->IsGroupObject())
460 SdrObjListIter aIter(pObj->GetSubList());
461 while ( aIter.IsMore() )
462 Removed( aIter.Next() );
467 void FmXUndoEnvironment::Removed(FmFormObj* pObj)
469 DBG_ASSERT( pObj, "FmXUndoEnvironment::Removed: invalid object!" );
470 if ( !pObj )
471 return;
473 // is the control still assigned to a form
474 Reference< XFormComponent > xContent(pObj->GetUnoControlModel(), UNO_QUERY);
475 if (!xContent.is())
476 return;
478 // The object is taken out of a list.
479 // If a father exists, the object is removed at the father and
480 // noted at the FormObject!
482 // If the object is reinserted and a parent exists, this parent is set though.
483 Reference< XIndexContainer > xForm(xContent->getParent(), UNO_QUERY);
484 if (!xForm.is())
485 return;
487 // determine which position the child was at
488 const sal_Int32 nPos = getElementPos(xForm, xContent);
489 if (nPos < 0)
490 return;
492 Sequence< ScriptEventDescriptor > aEvts;
493 Reference< XEventAttacherManager > xManager(xForm, UNO_QUERY);
494 if (xManager.is())
495 aEvts = xManager->getScriptEvents(nPos);
499 pObj->SetObjEnv(xForm, nPos, aEvts);
500 xForm->removeByIndex(nPos);
502 catch(Exception&)
504 DBG_UNHANDLED_EXCEPTION("svx");
508 // XEventListener
510 void SAL_CALL FmXUndoEnvironment::disposing(const EventObject& e)
512 // check if it's an object we have cached information about
513 if (m_pPropertySetCache)
515 Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
516 if (xSourceSet.is())
518 PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
519 PropertySetInfoCache::iterator aSetPos = pCache->find(xSourceSet);
520 if (aSetPos != pCache->end())
521 pCache->erase(aSetPos);
526 // XPropertyChangeListener
528 void SAL_CALL FmXUndoEnvironment::propertyChange(const PropertyChangeEvent& evt)
530 ::osl::ClearableMutexGuard aGuard( m_aMutex );
532 if (!IsLocked())
534 Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
535 if (!xSet.is())
536 return;
538 // if it's a "default value" property of a control model, set the according "value" property
539 static constexpr rtl::OUStringConstExpr pDefaultValueProperties[] = {
540 FM_PROP_DEFAULT_TEXT, FM_PROP_DEFAULTCHECKED, FM_PROP_DEFAULT_DATE, FM_PROP_DEFAULT_TIME,
541 FM_PROP_DEFAULT_VALUE, FM_PROP_DEFAULT_SELECT_SEQ, FM_PROP_EFFECTIVE_DEFAULT
543 static constexpr rtl::OUStringConstExpr aValueProperties[] = {
544 FM_PROP_TEXT, FM_PROP_STATE, FM_PROP_DATE, FM_PROP_TIME,
545 FM_PROP_VALUE, FM_PROP_SELECT_SEQ, FM_PROP_EFFECTIVE_VALUE
547 sal_Int32 nDefaultValueProps = SAL_N_ELEMENTS(pDefaultValueProperties);
548 OSL_ENSURE(SAL_N_ELEMENTS(aValueProperties) == nDefaultValueProps,
549 "FmXUndoEnvironment::propertyChange: inconsistence!");
550 for (sal_Int32 i=0; i<nDefaultValueProps; ++i)
552 if (evt.PropertyName == pDefaultValueProperties[i])
556 xSet->setPropertyValue(aValueProperties[i], evt.NewValue);
558 catch(const Exception&)
560 OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
565 // no Undo for transient and readonly props. But unfortunately "transient" is not only that the
566 // "transient" flag is set for the property in question, instead it is somewhat more complex
567 // Transience criterions are:
568 // - the "transient" flag is set for the property
569 // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
570 // to a database column. Note that it doesn't matter here whether the control actually
571 // *is* bound to a column
572 // - OR the control is bound to an external value via XBindableValue/XValueBinding
573 // which does not have a "ExternalData" property being <TRUE/>
575 if (!m_pPropertySetCache)
576 m_pPropertySetCache = new PropertySetInfoCache;
577 PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
579 // let's see if we know something about the set
580 PropertySetInfoCache::iterator aSetPos = pCache->find(xSet);
581 if (aSetPos == pCache->end())
583 PropertySetInfo aNewEntry;
584 if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xSet))
586 aNewEntry.bHasEmptyControlSource = false;
588 else
592 Any aCurrentControlSource = xSet->getPropertyValue(FM_PROP_CONTROLSOURCE);
593 aNewEntry.bHasEmptyControlSource = !aCurrentControlSource.hasValue() || ::comphelper::getString(aCurrentControlSource).isEmpty();
595 catch(const Exception&)
597 DBG_UNHANDLED_EXCEPTION("svx");
600 aSetPos = pCache->emplace(xSet,aNewEntry).first;
601 DBG_ASSERT(aSetPos != pCache->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
603 else
604 { // is it the DataField property ?
605 if (evt.PropertyName == FM_PROP_CONTROLSOURCE)
607 aSetPos->second.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
611 // now we have access to the cached info about the set
612 // let's see what we know about the property
613 PropertySetInfo::AllProperties& rPropInfos = aSetPos->second.aProps;
614 PropertySetInfo::AllProperties::iterator aPropertyPos = rPropInfos.find(evt.PropertyName);
615 if (aPropertyPos == rPropInfos.end())
616 { // nothing 'til now ... have to change this...
617 PropertyInfo aNewEntry;
619 // the attributes
620 sal_Int32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName(evt.PropertyName).Attributes;
621 aNewEntry.bIsTransientOrReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
623 // check if it is the special "DataFieldProperty"
624 aNewEntry.bIsValueProperty = false;
627 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY, xSet))
629 Any aControlSourceProperty = xSet->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY);
630 OUString sControlSourceProperty;
631 aControlSourceProperty >>= sControlSourceProperty;
633 aNewEntry.bIsValueProperty = (sControlSourceProperty == evt.PropertyName);
636 catch(const Exception&)
638 DBG_UNHANDLED_EXCEPTION("svx");
641 // insert the new entry
642 aPropertyPos = rPropInfos.emplace(evt.PropertyName,aNewEntry).first;
643 DBG_ASSERT(aPropertyPos != rPropInfos.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
646 // now we have access to the cached info about the property affected
647 // and are able to decide whether or not we need an undo action
649 bool bAddUndoAction = rModel.IsUndoEnabled();
650 // no UNDO for transient/readonly properties
651 if ( bAddUndoAction && aPropertyPos->second.bIsTransientOrReadOnly )
652 bAddUndoAction = false;
654 if ( bAddUndoAction && aPropertyPos->second.bIsValueProperty )
656 // no UNDO when the "value" property changes, but the ControlSource is non-empty
657 // (in this case the control is intended to be bound to a database column)
658 if ( !aSetPos->second.bHasEmptyControlSource )
659 bAddUndoAction = false;
661 // no UNDO if the control is currently bound to an external value
662 if ( bAddUndoAction )
664 Reference< XBindableValue > xBindable( evt.Source, UNO_QUERY );
665 Reference< XValueBinding > xBinding;
666 if ( xBindable.is() )
667 xBinding = xBindable->getValueBinding();
669 Reference< XPropertySet > xBindingProps;
670 Reference< XPropertySetInfo > xBindingPropsPSI;
671 if ( xBindable.is() )
672 xBindingProps.set( xBinding, UNO_QUERY );
673 if ( xBindingProps.is() )
674 xBindingPropsPSI = xBindingProps->getPropertySetInfo();
675 // TODO: we should cache all those things, else this might be too expensive.
676 // However, this requires we're notified of changes in the value binding
678 static constexpr OUStringLiteral s_sExternalData = u"ExternalData";
679 if ( xBindingPropsPSI.is() && xBindingPropsPSI->hasPropertyByName( s_sExternalData ) )
681 bool bExternalData = true;
682 OSL_VERIFY( xBindingProps->getPropertyValue( s_sExternalData ) >>= bExternalData );
683 bAddUndoAction = !bExternalData;
685 else
686 bAddUndoAction = !xBinding.is();
690 if ( bAddUndoAction && ( evt.PropertyName == FM_PROP_STRINGITEMLIST ) )
692 Reference< XListEntrySink > xSink( evt.Source, UNO_QUERY );
693 if ( xSink.is() && xSink->getListEntrySource().is() )
694 // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
695 bAddUndoAction = false;
698 if ( bAddUndoAction )
700 aGuard.clear();
701 // TODO: this is a potential race condition: two threads here could in theory
702 // add their undo actions out-of-order
704 SolarMutexGuard aSolarGuard;
705 rModel.AddUndo(std::make_unique<FmUndoPropertyAction>(rModel, evt));
708 else
710 // if it's the DataField property we may have to adjust our cache
711 if (m_pPropertySetCache && evt.PropertyName == FM_PROP_CONTROLSOURCE)
713 Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
714 PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
715 PropertySetInfo& rSetInfo = (*pCache)[xSet];
716 rSetInfo.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
721 // XContainerListener
723 void SAL_CALL FmXUndoEnvironment::elementInserted(const ContainerEvent& evt)
725 SolarMutexGuard aSolarGuard;
726 ::osl::MutexGuard aGuard( m_aMutex );
728 // new object for listening
729 Reference< XInterface > xIface;
730 evt.Element >>= xIface;
731 OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
732 AddElement(xIface);
734 implSetModified();
738 void FmXUndoEnvironment::implSetModified()
740 if ( !IsLocked() && rModel.GetObjectShell() )
742 rModel.GetObjectShell()->SetModified();
747 void SAL_CALL FmXUndoEnvironment::elementReplaced(const ContainerEvent& evt)
749 SolarMutexGuard aSolarGuard;
750 ::osl::MutexGuard aGuard( m_aMutex );
752 Reference< XInterface > xIface;
753 evt.ReplacedElement >>= xIface;
754 OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
755 RemoveElement(xIface);
757 evt.Element >>= xIface;
758 AddElement(xIface);
760 implSetModified();
764 void SAL_CALL FmXUndoEnvironment::elementRemoved(const ContainerEvent& evt)
766 SolarMutexGuard aSolarGuard;
767 ::osl::MutexGuard aGuard( m_aMutex );
769 Reference< XInterface > xIface( evt.Element, UNO_QUERY );
770 OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
771 RemoveElement(xIface);
773 implSetModified();
777 void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ )
779 implSetModified();
783 void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
785 Lock();
786 AddElement(Reference<XInterface>( rForms, UNO_QUERY ));
787 UnLock();
791 void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
793 Lock();
794 RemoveElement(Reference<XInterface>( rForms, UNO_QUERY ));
795 UnLock();
799 void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
801 // listen at the container
802 Reference< XIndexContainer > xContainer(Element, UNO_QUERY);
803 if (xContainer.is())
805 sal_uInt32 nCount = xContainer->getCount();
806 Reference< XInterface > xIface;
807 for (sal_uInt32 i = 0; i < nCount; i++)
809 xContainer->getByIndex(i) >>= xIface;
810 TogglePropertyListening(xIface);
814 Reference< XPropertySet > xSet(Element, UNO_QUERY);
815 if (xSet.is())
817 if (!bReadOnly)
818 xSet->addPropertyChangeListener( OUString(), this );
819 else
820 xSet->removePropertyChangeListener( OUString(), this );
825 void FmXUndoEnvironment::switchListening( const Reference< XIndexContainer >& _rxContainer, bool _bStartListening )
827 OSL_PRECOND( _rxContainer.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
828 if ( !_rxContainer.is() )
829 return;
833 // if it's an EventAttacherManager, then we need to listen for
834 // script events
835 Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY );
836 if ( xManager.is() )
838 if ( _bStartListening )
840 m_pScriptingEnv->registerEventAttacherManager( xManager );
841 if ( m_vbaListener.is() )
842 xManager->addScriptListener( m_vbaListener );
844 else
846 m_pScriptingEnv->revokeEventAttacherManager( xManager );
847 if ( m_vbaListener.is() )
848 xManager->removeScriptListener( m_vbaListener );
852 // also handle all children of this element
853 sal_uInt32 nCount = _rxContainer->getCount();
854 Reference< XInterface > xInterface;
855 for ( sal_uInt32 i = 0; i < nCount; ++i )
857 _rxContainer->getByIndex( i ) >>= xInterface;
858 if ( _bStartListening )
859 AddElement( xInterface );
860 else
861 RemoveElement( xInterface );
864 // be notified of any changes in the container elements
865 Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
866 OSL_ENSURE( xSimpleContainer.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
867 if ( xSimpleContainer.is() )
869 if ( _bStartListening )
870 xSimpleContainer->addContainerListener( this );
871 else
872 xSimpleContainer->removeContainerListener( this );
875 catch( const Exception& )
877 TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
882 void FmXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening )
884 OSL_PRECOND( _rxObject.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
888 if ( !bReadOnly )
890 Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
891 if ( xProps.is() )
893 if ( _bStartListening )
894 xProps->addPropertyChangeListener( OUString(), this );
895 else
896 xProps->removePropertyChangeListener( OUString(), this );
900 Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
901 if ( xBroadcaster.is() )
903 if ( _bStartListening )
904 xBroadcaster->addModifyListener( this );
905 else
906 xBroadcaster->removeModifyListener( this );
909 catch( const Exception& )
911 TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
916 void FmXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
918 OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
920 // listen at the container
921 Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
922 if ( xContainer.is() )
923 switchListening( xContainer, true );
925 switchListening( _rxElement, true );
929 void FmXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
931 if ( m_bDisposed )
932 return;
934 switchListening( _rxElement, false );
936 if (!bReadOnly)
938 // reset the ActiveConnection if the form is to be removed. This will (should) free the resources
939 // associated with this connection
940 // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
941 Reference< XForm > xForm( _rxElement, UNO_QUERY );
942 Reference< XPropertySet > xFormProperties( xForm, UNO_QUERY );
943 if ( xFormProperties.is() )
945 Reference< XConnection > xDummy;
946 if ( !isEmbeddedInDatabase( _rxElement, xDummy ) )
947 // (if there is a connection in the context of the component, setting
948 // a new connection would be vetoed, anyway)
949 // #i34196#
950 xFormProperties->setPropertyValue( FM_PROP_ACTIVE_CONNECTION, Any() );
954 Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
955 if ( xContainer.is() )
956 switchListening( xContainer, false );
960 FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel& rNewMod, const PropertyChangeEvent& evt)
961 :SdrUndoAction(rNewMod)
962 ,xObj(evt.Source, UNO_QUERY)
963 ,aPropertyName(evt.PropertyName)
964 ,aNewValue(evt.NewValue)
965 ,aOldValue(evt.OldValue)
967 if (rNewMod.GetObjectShell())
968 rNewMod.GetObjectShell()->SetModified();
969 if(static_STR_UNDO_PROPERTY.isEmpty())
970 static_STR_UNDO_PROPERTY = SvxResId(RID_STR_UNDO_PROPERTY);
974 void FmUndoPropertyAction::Undo()
976 FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
978 if (!xObj.is() || rEnv.IsLocked())
979 return;
981 rEnv.Lock();
984 xObj->setPropertyValue( aPropertyName, aOldValue );
986 catch( const Exception& )
988 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
990 rEnv.UnLock();
994 void FmUndoPropertyAction::Redo()
996 FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
998 if (!xObj.is() || rEnv.IsLocked())
999 return;
1001 rEnv.Lock();
1004 xObj->setPropertyValue( aPropertyName, aNewValue );
1006 catch( const Exception& )
1008 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
1010 rEnv.UnLock();
1014 OUString FmUndoPropertyAction::GetComment() const
1016 OUString aStr = static_STR_UNDO_PROPERTY.replaceFirst( "#", aPropertyName );
1017 return aStr;
1021 FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
1022 Action _eAction,
1023 const Reference< XIndexContainer > & xCont,
1024 const Reference< XInterface > & xElem,
1025 sal_Int32 nIdx)
1026 :SdrUndoAction( _rMod )
1027 ,m_xContainer( xCont )
1028 ,m_nIndex( nIdx )
1029 ,m_eAction( _eAction )
1031 OSL_ENSURE( nIdx >= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
1032 // some old code suggested this could be a valid argument. However, this code was
1033 // buggy, and it *seemed* that nobody used it - so it was removed.
1035 if ( !(xCont.is() && xElem.is()) )
1036 return;
1038 // normalize
1039 m_xElement = xElem;
1040 if ( m_eAction != Removed )
1041 return;
1043 if (m_nIndex >= 0)
1045 Reference< XEventAttacherManager > xManager( xCont, UNO_QUERY );
1046 if ( xManager.is() )
1047 m_aEvents = xManager->getScriptEvents(m_nIndex);
1049 else
1050 m_xElement = nullptr;
1052 // we now own the element
1053 m_xOwnElement = m_xElement;
1057 FmUndoContainerAction::~FmUndoContainerAction()
1059 // if we own the object...
1060 DisposeElement( m_xOwnElement );
1064 void FmUndoContainerAction::DisposeElement( const Reference< XInterface > & xElem )
1066 Reference< XComponent > xComp( xElem, UNO_QUERY );
1067 if ( xComp.is() )
1069 // and the object does not have a parent
1070 Reference< XChild > xChild( xElem, UNO_QUERY );
1071 if ( xChild.is() && !xChild->getParent().is() )
1072 // -> dispose it
1073 xComp->dispose();
1078 void FmUndoContainerAction::implReInsert( )
1080 if ( m_xContainer->getCount() < m_nIndex )
1081 return;
1083 // insert the element
1084 Any aVal;
1085 if ( m_xContainer->getElementType() == cppu::UnoType<XFormComponent>::get() )
1087 aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
1089 else
1091 aVal <<= Reference< XForm >( m_xElement, UNO_QUERY );
1093 m_xContainer->insertByIndex( m_nIndex, aVal );
1095 OSL_ENSURE( getElementPos( m_xContainer, m_xElement ) == m_nIndex, "FmUndoContainerAction::implReInsert: insertion did not work!" );
1097 // register the events
1098 Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
1099 if ( xManager.is() )
1100 xManager->registerScriptEvents( m_nIndex, m_aEvents );
1102 // we don't own the object anymore
1103 m_xOwnElement = nullptr;
1107 void FmUndoContainerAction::implReRemove( )
1109 Reference< XInterface > xElement;
1110 if ( ( m_nIndex >= 0 ) && ( m_nIndex < m_xContainer->getCount() ) )
1111 m_xContainer->getByIndex( m_nIndex ) >>= xElement;
1113 if ( xElement != m_xElement )
1115 // the indexes in the container changed. Okay, so go the long way and
1116 // manually determine the index
1117 m_nIndex = getElementPos( m_xContainer, m_xElement );
1118 if ( m_nIndex != -1 )
1119 xElement = m_xElement;
1122 OSL_ENSURE( xElement == m_xElement, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
1123 if ( xElement == m_xElement )
1125 Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
1126 if ( xManager.is() )
1127 m_aEvents = xManager->getScriptEvents( m_nIndex );
1128 m_xContainer->removeByIndex( m_nIndex );
1129 // from now on, we own this object
1130 m_xOwnElement = m_xElement;
1135 void FmUndoContainerAction::Undo()
1137 FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
1139 if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1140 return;
1142 rEnv.Lock();
1145 switch ( m_eAction )
1147 case Inserted:
1148 implReRemove();
1149 break;
1151 case Removed:
1152 implReInsert();
1153 break;
1156 catch( const Exception& )
1158 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
1160 rEnv.UnLock();
1164 void FmUndoContainerAction::Redo()
1166 FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
1167 if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1168 return;
1170 rEnv.Lock();
1173 switch ( m_eAction )
1175 case Inserted:
1176 implReInsert();
1177 break;
1179 case Removed:
1180 implReRemove();
1181 break;
1184 catch( const Exception& )
1186 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
1188 rEnv.UnLock();
1192 FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel& _rMod, SdrUnoObj* _pObject, const Reference< XControlModel > & _xReplaced)
1193 :SdrUndoAction(_rMod)
1194 ,m_xReplaced(_xReplaced)
1195 ,m_pObject(_pObject)
1200 FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
1202 // dispose our element if nobody else is responsible for
1203 DisposeElement(m_xReplaced);
1207 void FmUndoModelReplaceAction::DisposeElement( const css::uno::Reference< css::awt::XControlModel>& xReplaced )
1209 Reference< XComponent > xComp(xReplaced, UNO_QUERY);
1210 if (xComp.is())
1212 Reference< XChild > xChild(xReplaced, UNO_QUERY);
1213 if (!xChild.is() || !xChild->getParent().is())
1214 xComp->dispose();
1219 void FmUndoModelReplaceAction::Undo()
1223 Reference< XControlModel > xCurrentModel( m_pObject->GetUnoControlModel() );
1225 // replace the model within the parent
1226 Reference< XChild > xCurrentAsChild( xCurrentModel, UNO_QUERY );
1227 Reference< XNameContainer > xCurrentsParent;
1228 if ( xCurrentAsChild.is() )
1229 xCurrentsParent.set(xCurrentAsChild->getParent(), css::uno::UNO_QUERY);
1230 DBG_ASSERT( xCurrentsParent.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
1232 if ( xCurrentsParent.is() )
1234 // the form container works with FormComponents
1235 Reference< XFormComponent > xComponent( m_xReplaced, UNO_QUERY );
1236 DBG_ASSERT( xComponent.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
1238 Reference< XPropertySet > xCurrentAsSet( xCurrentModel, UNO_QUERY );
1239 DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME, xCurrentAsSet ), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
1241 OUString sName;
1242 xCurrentAsSet->getPropertyValue( FM_PROP_NAME ) >>= sName;
1243 xCurrentsParent->replaceByName( sName, Any( xComponent ) );
1245 m_pObject->SetUnoControlModel(m_xReplaced);
1246 m_pObject->SetChanged();
1248 m_xReplaced = xCurrentModel;
1251 catch(Exception&)
1253 OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
1258 OUString FmUndoModelReplaceAction::GetComment() const
1260 return SvxResId(RID_STR_UNDO_MODEL_REPLACE);
1263 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */