Simplify a bit
[LibreOffice.git] / svx / source / form / fmundo.cxx
blobb7bf173dba290c6df87eb2f9a36cf2bf5e90a66d
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>
44 #include <com/sun/star/script/XScriptListener.hpp>
46 #include <svx/fmtools.hxx>
47 #include <tools/debug.hxx>
48 #include <comphelper/diagnose_ex.hxx>
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>
56 #include <comphelper/processfactory.hxx>
57 #include <cppuhelper/implbase.hxx>
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::awt;
62 using namespace ::com::sun::star::beans;
63 using namespace ::com::sun::star::container;
64 using namespace ::com::sun::star::script;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::form;
67 using namespace ::com::sun::star::util;
68 using namespace ::com::sun::star::form::binding;
69 using namespace ::com::sun::star::sdbc;
70 using namespace ::svxform;
71 using namespace ::dbtools;
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 u"ooo.vba.EventListener"_ustr, 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(u"Model"_ustr, 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
157 struct PropertySetInfo
159 typedef std::map<OUString, PropertyInfo> AllProperties;
161 AllProperties aProps; // all properties of this set which we know so far
162 bool bHasEmptyControlSource; // sal_True -> the set has a DataField property, and the current value is an empty string
163 // sal_False -> the set has _no_ such property or its value isn't empty
166 static OUString static_STR_UNDO_PROPERTY;
169 FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel& _rModel)
170 :rModel( _rModel )
171 ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel ) )
172 ,m_Locks( 0 )
173 ,bReadOnly( false )
174 ,m_bDisposed( false )
178 m_vbaListener = new ScriptEventListenerWrapper( _rModel );
180 catch( Exception& )
185 FmXUndoEnvironment::~FmXUndoEnvironment()
187 if ( !m_bDisposed ) // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
188 m_pScriptingEnv->dispose();
191 void FmXUndoEnvironment::dispose()
193 OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::dispose: disposed twice?" );
194 if ( !m_bDisposed )
195 return;
197 Lock();
199 sal_uInt16 nCount = rModel.GetPageCount();
200 sal_uInt16 i;
201 for (i = 0; i < nCount; i++)
203 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
204 if ( pPage )
206 Reference< css::form::XForms > xForms = pPage->GetForms( false );
207 if ( xForms.is() )
208 RemoveElement( xForms );
212 nCount = rModel.GetMasterPageCount();
213 for (i = 0; i < nCount; i++)
215 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
216 if ( pPage )
218 Reference< css::form::XForms > xForms = pPage->GetForms( false );
219 if ( xForms.is() )
220 RemoveElement( xForms );
224 UnLock();
226 OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
227 if ( rModel.GetObjectShell() )
228 EndListening( *rModel.GetObjectShell() );
230 if ( IsListening( rModel ) )
231 EndListening( rModel );
233 m_pScriptingEnv->dispose();
235 m_bDisposed = true;
239 void FmXUndoEnvironment::ModeChanged()
241 OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
242 if ( !rModel.GetObjectShell() )
243 return;
245 if (bReadOnly == (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
246 return;
248 bReadOnly = !bReadOnly;
250 sal_uInt16 nCount = rModel.GetPageCount();
251 sal_uInt16 i;
252 for (i = 0; i < nCount; i++)
254 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
255 if ( pPage )
257 Reference< css::form::XForms > xForms = pPage->GetForms( false );
258 if ( xForms.is() )
259 TogglePropertyListening( xForms );
263 nCount = rModel.GetMasterPageCount();
264 for (i = 0; i < nCount; i++)
266 FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
267 if ( pPage )
269 Reference< css::form::XForms > xForms = pPage->GetForms( false );
270 if ( xForms.is() )
271 TogglePropertyListening( xForms );
275 if (!bReadOnly)
276 StartListening(rModel);
277 else
278 EndListening(rModel);
282 void FmXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
284 if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
286 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
287 switch (pSdrHint->GetKind())
289 case SdrHintKind::ObjectInserted:
291 SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
292 Inserted( pSdrObj );
293 } break;
294 case SdrHintKind::ObjectRemoved:
296 SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
297 Removed( pSdrObj );
299 break;
300 default:
301 break;
304 else if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
306 switch (static_cast<const SfxEventHint&>(rHint).GetEventId())
308 case SfxEventHintId::CreateDoc:
309 case SfxEventHintId::OpenDoc:
310 ModeChanged();
311 break;
312 default: break;
315 else if (rHint.GetId() != SfxHintId::NONE)
317 switch (rHint.GetId())
319 case SfxHintId::Dying:
320 dispose();
321 rModel.SetObjectShell( nullptr );
322 break;
323 case SfxHintId::ModeChanged:
324 ModeChanged();
325 break;
326 default: break;
331 void FmXUndoEnvironment::Inserted(SdrObject* pObj)
333 if (pObj->GetObjInventor() == SdrInventor::FmForm)
335 FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
336 Inserted( pFormObj );
338 else if (pObj->IsGroupObject())
340 SdrObjListIter aIter(pObj->GetSubList());
341 while ( aIter.IsMore() )
342 Inserted( aIter.Next() );
347 namespace
349 bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
351 if (!xCont.is() || !xElement.is())
352 return false;
354 sal_Int32 nCount = xCont->getCount();
355 Reference< XInterface > xComp;
356 for (sal_Int32 i = 0; i < nCount; i++)
360 xCont->getByIndex(i) >>= xComp;
361 if (xComp.is())
363 if ( xElement == xComp )
364 return true;
365 else
367 Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
368 if (xCont2.is() && lcl_searchElement(xCont2, xElement))
369 return true;
373 catch(const Exception&)
375 DBG_UNHANDLED_EXCEPTION("svx");
378 return false;
383 void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
385 DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
386 if ( !pObj )
387 return;
389 // is the control still assigned to a form
390 Reference< XInterface > xModel(pObj->GetUnoControlModel(), UNO_QUERY);
391 Reference< XFormComponent > xContent(xModel, UNO_QUERY);
392 if (!(xContent.is() && pObj->getSdrPageFromSdrObject()))
393 return;
395 // if the component doesn't belong to a form, yet, find one to insert into
396 if (!xContent->getParent().is())
400 const Reference< XIndexContainer >& xObjectParent = pObj->GetOriginalParent();
402 FmFormPage& rPage(dynamic_cast< FmFormPage& >( *pObj->getSdrPageFromSdrObject()));
403 Reference< XIndexAccess > xForms( rPage.GetForms(), UNO_QUERY_THROW );
405 Reference< XIndexContainer > xNewParent;
406 Reference< XForm > xForm;
407 sal_Int32 nPos = -1;
408 if ( lcl_searchElement( xForms, xObjectParent ) )
410 // the form which was the parent of the object when it was removed is still
411 // part of the form component hierarchy of the current page
412 xNewParent = xObjectParent;
413 xForm.set( xNewParent, UNO_QUERY_THROW );
414 nPos = ::std::min( pObj->GetOriginalIndex(), xNewParent->getCount() );
416 else
418 xForm.set( rPage.GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
419 xNewParent.set( xForm, UNO_QUERY_THROW );
420 nPos = xNewParent->getCount();
423 FmFormPageImpl::setUniqueName( xContent, xForm );
424 xNewParent->insertByIndex( nPos, Any( xContent ) );
426 Reference< XEventAttacherManager > xManager( xNewParent, UNO_QUERY_THROW );
427 xManager->registerScriptEvents( nPos, pObj->GetOriginalEvents() );
429 catch( const Exception& )
431 DBG_UNHANDLED_EXCEPTION("svx");
435 // reset FormObject
436 pObj->ClearObjEnv();
440 void FmXUndoEnvironment::Removed(SdrObject* pObj)
442 if ( pObj->IsVirtualObj() )
443 // for virtual objects, we've already been notified of the removal of the master
444 // object, which is sufficient here
445 return;
447 if (pObj->GetObjInventor() == SdrInventor::FmForm)
449 FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
450 Removed(pFormObj);
452 else if (pObj->IsGroupObject())
454 SdrObjListIter aIter(pObj->GetSubList());
455 while ( aIter.IsMore() )
456 Removed( aIter.Next() );
461 void FmXUndoEnvironment::Removed(FmFormObj* pObj)
463 DBG_ASSERT( pObj, "FmXUndoEnvironment::Removed: invalid object!" );
464 if ( !pObj )
465 return;
467 // is the control still assigned to a form
468 Reference< XFormComponent > xContent(pObj->GetUnoControlModel(), UNO_QUERY);
469 if (!xContent.is())
470 return;
472 // The object is taken out of a list.
473 // If a father exists, the object is removed at the father and
474 // noted at the FormObject!
476 // If the object is reinserted and a parent exists, this parent is set though.
477 Reference< XIndexContainer > xForm(xContent->getParent(), UNO_QUERY);
478 if (!xForm.is())
479 return;
481 // determine which position the child was at
482 const sal_Int32 nPos = getElementPos(xForm, xContent);
483 if (nPos < 0)
484 return;
486 Sequence< ScriptEventDescriptor > aEvts;
487 Reference< XEventAttacherManager > xManager(xForm, UNO_QUERY);
488 if (xManager.is())
489 aEvts = xManager->getScriptEvents(nPos);
493 pObj->SetObjEnv(xForm, nPos, aEvts);
494 xForm->removeByIndex(nPos);
496 catch(Exception&)
498 DBG_UNHANDLED_EXCEPTION("svx");
502 // XEventListener
504 void SAL_CALL FmXUndoEnvironment::disposing(const EventObject& e)
506 // check if it's an object we have cached information about
507 if (m_pPropertySetCache)
509 Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
510 if (xSourceSet.is())
512 PropertySetInfoCache::iterator aSetPos = m_pPropertySetCache->find(xSourceSet);
513 if (aSetPos != m_pPropertySetCache->end())
514 m_pPropertySetCache->erase(aSetPos);
519 // XPropertyChangeListener
521 void SAL_CALL FmXUndoEnvironment::propertyChange(const PropertyChangeEvent& evt)
523 ::osl::ClearableMutexGuard aGuard( m_aMutex );
525 if (!IsLocked())
527 Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
528 if (!xSet.is())
529 return;
531 // if it's a "default value" property of a control model, set the according "value" property
532 static constexpr OUString pDefaultValueProperties[] = {
533 FM_PROP_DEFAULT_TEXT, FM_PROP_DEFAULTCHECKED, FM_PROP_DEFAULT_DATE, FM_PROP_DEFAULT_TIME,
534 FM_PROP_DEFAULT_VALUE, FM_PROP_DEFAULT_SELECT_SEQ, FM_PROP_EFFECTIVE_DEFAULT
536 static constexpr OUString aValueProperties[] = {
537 FM_PROP_TEXT, FM_PROP_STATE, FM_PROP_DATE, FM_PROP_TIME,
538 FM_PROP_VALUE, FM_PROP_SELECT_SEQ, FM_PROP_EFFECTIVE_VALUE
540 sal_Int32 nDefaultValueProps = SAL_N_ELEMENTS(pDefaultValueProperties);
541 OSL_ENSURE(SAL_N_ELEMENTS(aValueProperties) == nDefaultValueProps,
542 "FmXUndoEnvironment::propertyChange: inconsistence!");
543 for (sal_Int32 i=0; i<nDefaultValueProps; ++i)
545 if (evt.PropertyName == pDefaultValueProperties[i])
549 xSet->setPropertyValue(aValueProperties[i], evt.NewValue);
551 catch(const Exception&)
553 OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
558 // no Undo for transient and readonly props. But unfortunately "transient" is not only that the
559 // "transient" flag is set for the property in question, instead it is somewhat more complex
560 // Transience criterions are:
561 // - the "transient" flag is set for the property
562 // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
563 // to a database column. Note that it doesn't matter here whether the control actually
564 // *is* bound to a column
565 // - OR the control is bound to an external value via XBindableValue/XValueBinding
566 // which does not have a "ExternalData" property being <TRUE/>
568 if (!m_pPropertySetCache)
569 m_pPropertySetCache = std::make_unique<PropertySetInfoCache>();
571 // let's see if we know something about the set
572 PropertySetInfoCache::iterator aSetPos = m_pPropertySetCache->find(xSet);
573 if (aSetPos == m_pPropertySetCache->end())
575 PropertySetInfo aNewEntry;
576 if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xSet))
578 aNewEntry.bHasEmptyControlSource = false;
580 else
584 Any aCurrentControlSource = xSet->getPropertyValue(FM_PROP_CONTROLSOURCE);
585 aNewEntry.bHasEmptyControlSource = !aCurrentControlSource.hasValue() || ::comphelper::getString(aCurrentControlSource).isEmpty();
587 catch(const Exception&)
589 DBG_UNHANDLED_EXCEPTION("svx");
592 aSetPos = m_pPropertySetCache->emplace(xSet,aNewEntry).first;
593 DBG_ASSERT(aSetPos != m_pPropertySetCache->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
595 else
596 { // is it the DataField property ?
597 if (evt.PropertyName == FM_PROP_CONTROLSOURCE)
599 aSetPos->second.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
603 // now we have access to the cached info about the set
604 // let's see what we know about the property
605 PropertySetInfo::AllProperties& rPropInfos = aSetPos->second.aProps;
606 PropertySetInfo::AllProperties::iterator aPropertyPos = rPropInfos.find(evt.PropertyName);
607 if (aPropertyPos == rPropInfos.end())
608 { // nothing 'til now ... have to change this...
609 PropertyInfo aNewEntry;
611 // the attributes
612 sal_Int32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName(evt.PropertyName).Attributes;
613 aNewEntry.bIsTransientOrReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
615 // check if it is the special "DataFieldProperty"
616 aNewEntry.bIsValueProperty = false;
619 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY, xSet))
621 Any aControlSourceProperty = xSet->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY);
622 OUString sControlSourceProperty;
623 aControlSourceProperty >>= sControlSourceProperty;
625 aNewEntry.bIsValueProperty = (sControlSourceProperty == evt.PropertyName);
628 catch(const Exception&)
630 DBG_UNHANDLED_EXCEPTION("svx");
633 // insert the new entry
634 aPropertyPos = rPropInfos.emplace(evt.PropertyName,aNewEntry).first;
635 DBG_ASSERT(aPropertyPos != rPropInfos.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
638 // now we have access to the cached info about the property affected
639 // and are able to decide whether or not we need an undo action
641 bool bAddUndoAction = rModel.IsUndoEnabled();
642 // no UNDO for transient/readonly properties
643 if ( bAddUndoAction && aPropertyPos->second.bIsTransientOrReadOnly )
644 bAddUndoAction = false;
646 if ( bAddUndoAction && aPropertyPos->second.bIsValueProperty )
648 // no UNDO when the "value" property changes, but the ControlSource is non-empty
649 // (in this case the control is intended to be bound to a database column)
650 if ( !aSetPos->second.bHasEmptyControlSource )
651 bAddUndoAction = false;
653 // no UNDO if the control is currently bound to an external value
654 if ( bAddUndoAction )
656 Reference< XBindableValue > xBindable( evt.Source, UNO_QUERY );
657 Reference< XValueBinding > xBinding;
658 if ( xBindable.is() )
659 xBinding = xBindable->getValueBinding();
661 Reference< XPropertySet > xBindingProps;
662 Reference< XPropertySetInfo > xBindingPropsPSI;
663 if ( xBindable.is() )
664 xBindingProps.set( xBinding, UNO_QUERY );
665 if ( xBindingProps.is() )
666 xBindingPropsPSI = xBindingProps->getPropertySetInfo();
667 // TODO: we should cache all those things, else this might be too expensive.
668 // However, this requires we're notified of changes in the value binding
670 static constexpr OUString s_sExternalData = u"ExternalData"_ustr;
671 if ( xBindingPropsPSI.is() && xBindingPropsPSI->hasPropertyByName( s_sExternalData ) )
673 bool bExternalData = true;
674 OSL_VERIFY( xBindingProps->getPropertyValue( s_sExternalData ) >>= bExternalData );
675 bAddUndoAction = !bExternalData;
677 else
678 bAddUndoAction = !xBinding.is();
682 if ( bAddUndoAction && ( evt.PropertyName == FM_PROP_STRINGITEMLIST ) )
684 Reference< XListEntrySink > xSink( evt.Source, UNO_QUERY );
685 if ( xSink.is() && xSink->getListEntrySource().is() )
686 // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
687 bAddUndoAction = false;
690 if ( bAddUndoAction )
692 aGuard.clear();
693 // TODO: this is a potential race condition: two threads here could in theory
694 // add their undo actions out-of-order
696 SolarMutexGuard aSolarGuard;
697 rModel.AddUndo(std::make_unique<FmUndoPropertyAction>(rModel, evt));
700 else
702 // if it's the DataField property we may have to adjust our cache
703 if (m_pPropertySetCache && evt.PropertyName == FM_PROP_CONTROLSOURCE)
705 Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
706 PropertySetInfo& rSetInfo = (*m_pPropertySetCache)[xSet];
707 rSetInfo.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
712 // XContainerListener
714 void SAL_CALL FmXUndoEnvironment::elementInserted(const ContainerEvent& evt)
716 SolarMutexGuard aSolarGuard;
717 ::osl::MutexGuard aGuard( m_aMutex );
719 // new object for listening
720 Reference< XInterface > xIface;
721 evt.Element >>= xIface;
722 OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
723 AddElement(xIface);
725 implSetModified();
729 void FmXUndoEnvironment::implSetModified()
731 if ( !IsLocked() && rModel.GetObjectShell() )
733 rModel.GetObjectShell()->SetModified();
738 void SAL_CALL FmXUndoEnvironment::elementReplaced(const ContainerEvent& evt)
740 SolarMutexGuard aSolarGuard;
741 ::osl::MutexGuard aGuard( m_aMutex );
743 Reference< XInterface > xIface;
744 evt.ReplacedElement >>= xIface;
745 OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
746 RemoveElement(xIface);
748 evt.Element >>= xIface;
749 AddElement(xIface);
751 implSetModified();
755 void SAL_CALL FmXUndoEnvironment::elementRemoved(const ContainerEvent& evt)
757 SolarMutexGuard aSolarGuard;
758 ::osl::MutexGuard aGuard( m_aMutex );
760 Reference< XInterface > xIface( evt.Element, UNO_QUERY );
761 OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
762 RemoveElement(xIface);
764 implSetModified();
768 void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ )
770 implSetModified();
774 void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
776 Lock();
777 AddElement(Reference<XInterface>( rForms, UNO_QUERY ));
778 UnLock();
782 void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
784 Lock();
785 RemoveElement(Reference<XInterface>( rForms, UNO_QUERY ));
786 UnLock();
790 void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
792 // listen at the container
793 Reference< XIndexContainer > xContainer(Element, UNO_QUERY);
794 if (xContainer.is())
796 sal_uInt32 nCount = xContainer->getCount();
797 Reference< XInterface > xIface;
798 for (sal_uInt32 i = 0; i < nCount; i++)
800 xContainer->getByIndex(i) >>= xIface;
801 TogglePropertyListening(xIface);
805 Reference< XPropertySet > xSet(Element, UNO_QUERY);
806 if (xSet.is())
808 if (!bReadOnly)
809 xSet->addPropertyChangeListener( OUString(), this );
810 else
811 xSet->removePropertyChangeListener( OUString(), this );
816 void FmXUndoEnvironment::switchListening( const Reference< XIndexContainer >& _rxContainer, bool _bStartListening )
818 OSL_PRECOND( _rxContainer.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
819 if ( !_rxContainer.is() )
820 return;
824 // if it's an EventAttacherManager, then we need to listen for
825 // script events
826 Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY );
827 if ( xManager.is() )
829 if ( _bStartListening )
831 m_pScriptingEnv->registerEventAttacherManager( xManager );
832 if ( m_vbaListener.is() )
833 xManager->addScriptListener( m_vbaListener );
835 else
837 m_pScriptingEnv->revokeEventAttacherManager( xManager );
838 if ( m_vbaListener.is() )
839 xManager->removeScriptListener( m_vbaListener );
843 // also handle all children of this element
844 sal_uInt32 nCount = _rxContainer->getCount();
845 Reference< XInterface > xInterface;
846 for ( sal_uInt32 i = 0; i < nCount; ++i )
848 _rxContainer->getByIndex( i ) >>= xInterface;
849 if ( _bStartListening )
850 AddElement( xInterface );
851 else
852 RemoveElement( xInterface );
855 // be notified of any changes in the container elements
856 Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
857 OSL_ENSURE( xSimpleContainer.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
858 if ( xSimpleContainer.is() )
860 if ( _bStartListening )
861 xSimpleContainer->addContainerListener( this );
862 else
863 xSimpleContainer->removeContainerListener( this );
866 catch( const Exception& )
868 TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
873 void FmXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening )
875 OSL_PRECOND( _rxObject.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
879 if ( !bReadOnly )
881 Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
882 if ( xProps.is() )
884 if ( _bStartListening )
885 xProps->addPropertyChangeListener( OUString(), this );
886 else
887 xProps->removePropertyChangeListener( OUString(), this );
891 Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
892 if ( xBroadcaster.is() )
894 if ( _bStartListening )
895 xBroadcaster->addModifyListener( this );
896 else
897 xBroadcaster->removeModifyListener( this );
900 catch( const Exception& )
902 TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
907 void FmXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
909 OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
911 // listen at the container
912 Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
913 if ( xContainer.is() )
914 switchListening( xContainer, true );
916 switchListening( _rxElement, true );
920 void FmXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
922 if ( m_bDisposed )
923 return;
925 switchListening( _rxElement, false );
927 if (!bReadOnly)
929 // reset the ActiveConnection if the form is to be removed. This will (should) free the resources
930 // associated with this connection
931 // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
932 Reference< XForm > xForm( _rxElement, UNO_QUERY );
933 Reference< XPropertySet > xFormProperties( xForm, UNO_QUERY );
934 if ( xFormProperties.is() )
936 Reference< XConnection > xDummy;
937 if ( !isEmbeddedInDatabase( _rxElement, xDummy ) )
938 // (if there is a connection in the context of the component, setting
939 // a new connection would be vetoed, anyway)
940 // #i34196#
941 xFormProperties->setPropertyValue( FM_PROP_ACTIVE_CONNECTION, Any() );
945 Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
946 if ( xContainer.is() )
947 switchListening( xContainer, false );
951 FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel& rNewMod, const PropertyChangeEvent& evt)
952 :SdrUndoAction(rNewMod)
953 ,xObj(evt.Source, UNO_QUERY)
954 ,aPropertyName(evt.PropertyName)
955 ,aNewValue(evt.NewValue)
956 ,aOldValue(evt.OldValue)
958 if (rNewMod.GetObjectShell())
959 rNewMod.GetObjectShell()->SetModified();
960 if(static_STR_UNDO_PROPERTY.isEmpty())
961 static_STR_UNDO_PROPERTY = SvxResId(RID_STR_UNDO_PROPERTY);
965 void FmUndoPropertyAction::Undo()
967 FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(m_rMod).GetUndoEnv();
969 if (!xObj.is() || rEnv.IsLocked())
970 return;
972 rEnv.Lock();
975 xObj->setPropertyValue( aPropertyName, aOldValue );
977 catch( const Exception& )
979 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
981 rEnv.UnLock();
985 void FmUndoPropertyAction::Redo()
987 FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(m_rMod).GetUndoEnv();
989 if (!xObj.is() || rEnv.IsLocked())
990 return;
992 rEnv.Lock();
995 xObj->setPropertyValue( aPropertyName, aNewValue );
997 catch( const Exception& )
999 TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
1001 rEnv.UnLock();
1005 OUString FmUndoPropertyAction::GetComment() const
1007 OUString aStr = static_STR_UNDO_PROPERTY.replaceFirst( "#", aPropertyName );
1008 return aStr;
1012 FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
1013 Action _eAction,
1014 const Reference< XIndexContainer > & xCont,
1015 const Reference< XInterface > & xElem,
1016 sal_Int32 nIdx)
1017 :SdrUndoAction( _rMod )
1018 ,m_xContainer( xCont )
1019 ,m_nIndex( nIdx )
1020 ,m_eAction( _eAction )
1022 OSL_ENSURE( nIdx >= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
1023 // some old code suggested this could be a valid argument. However, this code was
1024 // buggy, and it *seemed* that nobody used it - so it was removed.
1026 if ( !(xCont.is() && xElem.is()) )
1027 return;
1029 // normalize
1030 m_xElement = xElem;
1031 if ( m_eAction != Removed )
1032 return;
1034 if (m_nIndex >= 0)
1036 Reference< XEventAttacherManager > xManager( xCont, UNO_QUERY );
1037 if ( xManager.is() )
1038 m_aEvents = xManager->getScriptEvents(m_nIndex);
1040 else
1041 m_xElement = nullptr;
1043 // we now own the element
1044 m_xOwnElement = m_xElement;
1048 FmUndoContainerAction::~FmUndoContainerAction()
1050 // if we own the object...
1051 DisposeElement( m_xOwnElement );
1055 void FmUndoContainerAction::DisposeElement( const Reference< XInterface > & xElem )
1057 Reference< XComponent > xComp( xElem, UNO_QUERY );
1058 if ( xComp.is() )
1060 // and the object does not have a parent
1061 Reference< XChild > xChild( xElem, UNO_QUERY );
1062 if ( xChild.is() && !xChild->getParent().is() )
1063 // -> dispose it
1064 xComp->dispose();
1069 void FmUndoContainerAction::implReInsert( )
1071 if ( m_xContainer->getCount() < m_nIndex )
1072 return;
1074 // insert the element
1075 Any aVal;
1076 if ( m_xContainer->getElementType() == cppu::UnoType<XFormComponent>::get() )
1078 aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
1080 else
1082 aVal <<= Reference< XForm >( m_xElement, UNO_QUERY );
1084 m_xContainer->insertByIndex( m_nIndex, aVal );
1086 OSL_ENSURE( getElementPos( m_xContainer, m_xElement ) == m_nIndex, "FmUndoContainerAction::implReInsert: insertion did not work!" );
1088 // register the events
1089 Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
1090 if ( xManager.is() )
1091 xManager->registerScriptEvents( m_nIndex, m_aEvents );
1093 // we don't own the object anymore
1094 m_xOwnElement = nullptr;
1098 void FmUndoContainerAction::implReRemove( )
1100 Reference< XInterface > xElement;
1101 if ( ( m_nIndex >= 0 ) && ( m_nIndex < m_xContainer->getCount() ) )
1102 m_xContainer->getByIndex( m_nIndex ) >>= xElement;
1104 if ( xElement != m_xElement )
1106 // the indexes in the container changed. Okay, so go the long way and
1107 // manually determine the index
1108 m_nIndex = getElementPos( m_xContainer, m_xElement );
1109 if ( m_nIndex != -1 )
1110 xElement = m_xElement;
1113 OSL_ENSURE( xElement == m_xElement, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
1114 if ( xElement == m_xElement )
1116 Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
1117 if ( xManager.is() )
1118 m_aEvents = xManager->getScriptEvents( m_nIndex );
1119 m_xContainer->removeByIndex( m_nIndex );
1120 // from now on, we own this object
1121 m_xOwnElement = m_xElement;
1126 void FmUndoContainerAction::Undo()
1128 FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( m_rMod ).GetUndoEnv();
1130 if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1131 return;
1133 rEnv.Lock();
1136 switch ( m_eAction )
1138 case Inserted:
1139 implReRemove();
1140 break;
1142 case Removed:
1143 implReInsert();
1144 break;
1147 catch( const Exception& )
1149 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
1151 rEnv.UnLock();
1155 void FmUndoContainerAction::Redo()
1157 FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( m_rMod ).GetUndoEnv();
1158 if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1159 return;
1161 rEnv.Lock();
1164 switch ( m_eAction )
1166 case Inserted:
1167 implReInsert();
1168 break;
1170 case Removed:
1171 implReRemove();
1172 break;
1175 catch( const Exception& )
1177 TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
1179 rEnv.UnLock();
1183 FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel& _rMod, SdrUnoObj* _pObject, const Reference< XControlModel > & _xReplaced)
1184 :SdrUndoAction(_rMod)
1185 ,m_xReplaced(_xReplaced)
1186 ,m_pObject(_pObject)
1191 FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
1193 // dispose our element if nobody else is responsible for
1194 DisposeElement(m_xReplaced);
1198 void FmUndoModelReplaceAction::DisposeElement( const css::uno::Reference< css::awt::XControlModel>& xReplaced )
1200 Reference< XComponent > xComp(xReplaced, UNO_QUERY);
1201 if (xComp.is())
1203 Reference< XChild > xChild(xReplaced, UNO_QUERY);
1204 if (!xChild.is() || !xChild->getParent().is())
1205 xComp->dispose();
1210 void FmUndoModelReplaceAction::Undo()
1214 Reference< XControlModel > xCurrentModel( m_pObject->GetUnoControlModel() );
1216 // replace the model within the parent
1217 Reference< XChild > xCurrentAsChild( xCurrentModel, UNO_QUERY );
1218 Reference< XNameContainer > xCurrentsParent;
1219 if ( xCurrentAsChild.is() )
1220 xCurrentsParent.set(xCurrentAsChild->getParent(), css::uno::UNO_QUERY);
1221 DBG_ASSERT( xCurrentsParent.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
1223 if ( xCurrentsParent.is() )
1225 // the form container works with FormComponents
1226 Reference< XFormComponent > xComponent( m_xReplaced, UNO_QUERY );
1227 DBG_ASSERT( xComponent.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
1229 Reference< XPropertySet > xCurrentAsSet( xCurrentModel, UNO_QUERY );
1230 DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME, xCurrentAsSet ), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
1232 OUString sName;
1233 xCurrentAsSet->getPropertyValue( FM_PROP_NAME ) >>= sName;
1234 xCurrentsParent->replaceByName( sName, Any( xComponent ) );
1236 m_pObject->SetUnoControlModel(m_xReplaced);
1237 m_pObject->SetChanged();
1239 m_xReplaced = xCurrentModel;
1242 catch(Exception&)
1244 OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
1249 OUString FmUndoModelReplaceAction::GetComment() const
1251 return SvxResId(RID_STR_UNDO_MODEL_REPLACE);
1254 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */