Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / form / fmundo.cxx
blobe934e7c6574cb80e7015838dd698eea749ddd565
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>
23 #include <string_view>
25 #include <sal/macros.h>
26 #include <fmundo.hxx>
27 #include <fmpgeimp.hxx>
28 #include <svx/svditer.hxx>
29 #include <fmobj.hxx>
30 #include <fmprop.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>
75 namespace {
77 class ScriptEventListenerWrapper : public cppu::WeakImplHelper< XScriptListener >
79 public:
80 /// @throws css::uno::RuntimeException
81 explicit ScriptEventListenerWrapper( FmFormModel& _rModel)
82 :m_rModel( _rModel )
83 ,m_attemptedListenerCreation( false )
87 // XEventListener
88 virtual void SAL_CALL disposing(const EventObject& ) override {}
90 // XScriptListener
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 );
107 return Any();
110 private:
111 void attemptListenerCreation()
113 if ( m_attemptedListenerCreation )
114 return;
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),
124 UNO_QUERY_THROW);
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
148 struct PropertyInfo
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)
173 :rModel( _rModel )
174 ,m_pPropertySetCache( nullptr )
175 ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel ) )
176 ,m_Locks( 0 )
177 ,bReadOnly( false )
178 ,m_bDisposed( false )
182 m_vbaListener = new ScriptEventListenerWrapper( _rModel );
184 catch( Exception& )
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?" );
201 if ( !m_bDisposed )
202 return;
204 Lock();
206 sal_uInt16 nCount = rModel.GetPageCount();
207 sal_uInt16 i;
208 for (i = 0; i < nCount; i++)
210 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
211 if ( pPage )
213 Reference< css::form::XForms > xForms = pPage->GetForms( false ).get();
214 if ( xForms.is() )
215 RemoveElement( xForms );
219 nCount = rModel.GetMasterPageCount();
220 for (i = 0; i < nCount; i++)
222 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
223 if ( pPage )
225 Reference< css::form::XForms > xForms = pPage->GetForms( false ).get();
226 if ( xForms.is() )
227 RemoveElement( xForms );
231 UnLock();
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();
242 m_bDisposed = true;
246 void FmXUndoEnvironment::ModeChanged()
248 OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
249 if ( !rModel.GetObjectShell() )
250 return;
252 if (bReadOnly == (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
253 return;
255 bReadOnly = !bReadOnly;
257 sal_uInt16 nCount = rModel.GetPageCount();
258 sal_uInt16 i;
259 for (i = 0; i < nCount; i++)
261 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
262 if ( pPage )
264 Reference< css::form::XForms > xForms = pPage->GetForms( false ).get();
265 if ( xForms.is() )
266 TogglePropertyListening( xForms );
270 nCount = rModel.GetMasterPageCount();
271 for (i = 0; i < nCount; i++)
273 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
274 if ( pPage )
276 Reference< css::form::XForms > xForms = pPage->GetForms( false ).get();
277 if ( xForms.is() )
278 TogglePropertyListening( xForms );
282 if (!bReadOnly)
283 StartListening(rModel);
284 else
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());
299 Inserted( pSdrObj );
300 } break;
301 case SdrHintKind::ObjectRemoved:
303 SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
304 Removed( pSdrObj );
306 break;
307 default:
308 break;
311 else if (rHint.GetId() != SfxHintId::NONE)
313 switch (rHint.GetId())
315 case SfxHintId::Dying:
316 dispose();
317 rModel.SetObjectShell( nullptr );
318 break;
319 case SfxHintId::ModeChanged:
320 ModeChanged();
321 break;
322 default: break;
325 else if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint))
327 switch (pEventHint->GetEventId())
329 case SfxEventHintId::CreateDoc:
330 case SfxEventHintId::OpenDoc:
331 ModeChanged();
332 break;
333 default: break;
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() );
354 namespace
356 bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
358 if (!xCont.is() || !xElement.is())
359 return false;
361 sal_Int32 nCount = xCont->getCount();
362 Reference< XInterface > xComp;
363 for (sal_Int32 i = 0; i < nCount; i++)
367 xCont->getByIndex(i) >>= xComp;
368 if (xComp.is())
370 if ( xElement == xComp )
371 return true;
372 else
374 Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
375 if (xCont2.is() && lcl_searchElement(xCont2, xElement))
376 return true;
380 catch(const Exception&)
382 DBG_UNHANDLED_EXCEPTION("svx");
385 return false;
390 void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
392 DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
393 if ( !pObj )
394 return;
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()))
400 return;
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;
414 sal_Int32 nPos = -1;
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() );
423 else
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");
442 // reset FormObject
443 pObj->ClearObjEnv();
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
452 return;
454 if (pObj->GetObjInventor() == SdrInventor::FmForm)
456 FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
457 Removed(pFormObj);
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!" );
471 if ( !pObj )
472 return;
474 // is the control still assigned to a form
475 Reference< XFormComponent > xContent(pObj->GetUnoControlModel(), UNO_QUERY);
476 if (!xContent.is())
477 return;
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);
485 if (!xForm.is())
486 return;
488 Reference< XIndexAccess > xIndexAccess(xForm.get());
489 // determine which position the child was at
490 const sal_Int32 nPos = getElementPos(xIndexAccess, xContent);
491 if (nPos < 0)
492 return;
494 Sequence< ScriptEventDescriptor > aEvts;
495 Reference< XEventAttacherManager > xManager(xForm, UNO_QUERY);
496 if (xManager.is())
497 aEvts = xManager->getScriptEvents(nPos);
501 pObj->SetObjEnv(xForm, nPos, aEvts);
502 xForm->removeByIndex(nPos);
504 catch(Exception&)
506 DBG_UNHANDLED_EXCEPTION("svx");
510 // XEventListener
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);
518 if (xSourceSet.is())
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 );
534 if (!IsLocked())
536 Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
537 if (!xSet.is())
538 return;
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;
590 else
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 ?");
605 else
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;
621 // the attributes
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;
687 else
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 )
702 aGuard.clear();
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));
710 else
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!");
734 AddElement(xIface);
736 implSetModified();
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;
760 AddElement(xIface);
762 implSetModified();
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);
775 implSetModified();
779 void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ )
781 implSetModified();
785 void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
787 Lock();
788 AddElement(Reference<XInterface>( rForms, UNO_QUERY ));
789 UnLock();
793 void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
795 Lock();
796 RemoveElement(Reference<XInterface>( rForms, UNO_QUERY ));
797 UnLock();
801 void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
803 // listen at the container
804 Reference< XIndexContainer > xContainer(Element, UNO_QUERY);
805 if (xContainer.is())
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);
817 if (xSet.is())
819 if (!bReadOnly)
820 xSet->addPropertyChangeListener( OUString(), this );
821 else
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() )
831 return;
835 // if it's an EventAttacherManager, then we need to listen for
836 // script events
837 Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY );
838 if ( xManager.is() )
840 if ( _bStartListening )
842 m_pScriptingEnv->registerEventAttacherManager( xManager );
843 if ( m_vbaListener.is() )
844 xManager->addScriptListener( m_vbaListener );
846 else
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 );
862 else
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 );
873 else
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?" );
890 if ( !bReadOnly )
892 Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
893 if ( xProps.is() )
895 if ( _bStartListening )
896 xProps->addPropertyChangeListener( OUString(), this );
897 else
898 xProps->removePropertyChangeListener( OUString(), this );
902 Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
903 if ( xBroadcaster.is() )
905 if ( _bStartListening )
906 xBroadcaster->addModifyListener( this );
907 else
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)
933 if ( m_bDisposed )
934 return;
936 switchListening( _rxElement, false );
938 if (!bReadOnly)
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)
951 // #i34196#
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())
981 return;
983 rEnv.Lock();
986 xObj->setPropertyValue( aPropertyName, aOldValue );
988 catch( const Exception& )
990 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
992 rEnv.UnLock();
996 void FmUndoPropertyAction::Redo()
998 FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
1000 if (!xObj.is() || rEnv.IsLocked())
1001 return;
1003 rEnv.Lock();
1006 xObj->setPropertyValue( aPropertyName, aNewValue );
1008 catch( const Exception& )
1010 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
1012 rEnv.UnLock();
1016 OUString FmUndoPropertyAction::GetComment() const
1018 OUString aStr = static_STR_UNDO_PROPERTY.replaceFirst( "#", aPropertyName );
1019 return aStr;
1023 FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
1024 Action _eAction,
1025 const Reference< XIndexContainer > & xCont,
1026 const Reference< XInterface > & xElem,
1027 sal_Int32 nIdx)
1028 :SdrUndoAction( _rMod )
1029 ,m_xContainer( xCont )
1030 ,m_nIndex( nIdx )
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()) )
1038 return;
1040 // normalize
1041 m_xElement = xElem;
1042 if ( m_eAction != Removed )
1043 return;
1045 if (m_nIndex >= 0)
1047 Reference< XEventAttacherManager > xManager( xCont, UNO_QUERY );
1048 if ( xManager.is() )
1049 m_aEvents = xManager->getScriptEvents(m_nIndex);
1051 else
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 );
1069 if ( xComp.is() )
1071 // and the object does not have a parent
1072 Reference< XChild > xChild( xElem, UNO_QUERY );
1073 if ( xChild.is() && !xChild->getParent().is() )
1074 // -> dispose it
1075 xComp->dispose();
1080 void FmUndoContainerAction::implReInsert( )
1082 if ( m_xContainer->getCount() < m_nIndex )
1083 return;
1085 // insert the element
1086 Any aVal;
1087 if ( m_xContainer->getElementType() == cppu::UnoType<XFormComponent>::get() )
1089 aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
1091 else
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()) )
1142 return;
1144 rEnv.Lock();
1147 switch ( m_eAction )
1149 case Inserted:
1150 implReRemove();
1151 break;
1153 case Removed:
1154 implReInsert();
1155 break;
1158 catch( const Exception& )
1160 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
1162 rEnv.UnLock();
1166 void FmUndoContainerAction::Redo()
1168 FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
1169 if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1170 return;
1172 rEnv.Lock();
1175 switch ( m_eAction )
1177 case Inserted:
1178 implReInsert();
1179 break;
1181 case Removed:
1182 implReRemove();
1183 break;
1186 catch( const Exception& )
1188 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
1190 rEnv.UnLock();
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);
1212 if (xComp.is())
1214 Reference< XChild > xChild(xReplaced, UNO_QUERY);
1215 if (!xChild.is() || !xChild->getParent().is())
1216 xComp->dispose();
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 !");
1243 OUString sName;
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;
1253 catch(Exception&)
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: */