xref: /trunk/main/svx/source/form/fmundo.cxx (revision f6e50924)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 
27 #include "fmundo.hxx"
28 #include "fmpgeimp.hxx"
29 #include "svx/dbtoolsclient.hxx"
30 #include "svx/svditer.hxx"
31 #include "fmobj.hxx"
32 #include "fmprop.hrc"
33 #include "svx/fmresids.hrc"
34 #include "svx/fmglob.hxx"
35 #include "svx/dialmgr.hxx"
36 #include "svx/fmmodel.hxx"
37 #include "svx/fmpage.hxx"
38 
39 /** === begin UNO includes === **/
40 #include <com/sun/star/util/XModifyBroadcaster.hpp>
41 #include <com/sun/star/beans/PropertyAttribute.hpp>
42 #include <com/sun/star/container/XContainer.hpp>
43 #include <com/sun/star/container/XContainerListener.hpp>
44 #include <com/sun/star/script/XEventAttacherManager.hpp>
45 #include <com/sun/star/form/binding/XBindableValue.hpp>
46 #include <com/sun/star/form/binding/XListEntrySink.hpp>
47 #include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp>
48 /** === end UNO includes === **/
49 
50 #include "svx/fmtools.hxx"
51 #include <rtl/logfile.hxx>
52 #include <svl/macitem.hxx>
53 #include <tools/shl.hxx>
54 #include <tools/diagnose_ex.h>
55 #include <sfx2/objsh.hxx>
56 #include <sfx2/docfile.hxx>
57 #include <sfx2/app.hxx>
58 #include <sfx2/sfx.hrc>
59 #include <sfx2/event.hxx>
60 #include <osl/mutex.hxx>
61 #include <vos/mutex.hxx>
62 #include <comphelper/property.hxx>
63 #include <comphelper/uno3.hxx>
64 #include <comphelper/stl_types.hxx>
65 #include <comphelper/componentcontext.hxx>
66 
67 using namespace ::com::sun::star::uno;
68 using namespace ::com::sun::star::awt;
69 using namespace ::com::sun::star::beans;
70 using namespace ::com::sun::star::container;
71 using namespace ::com::sun::star::script;
72 using namespace ::com::sun::star::lang;
73 using namespace ::com::sun::star::form;
74 using namespace ::com::sun::star::util;
75 using namespace ::com::sun::star::reflection;
76 using namespace ::com::sun::star::form::binding;
77 using namespace ::svxform;
78 
79 
80 #include <com/sun/star/script/XScriptListener.hdl>
81 #include <comphelper/processfactory.hxx>
82 #include <cppuhelper/implbase1.hxx>
83 
84 typedef cppu::WeakImplHelper1< XScriptListener > ScriptEventListener_BASE;
85 class ScriptEventListenerWrapper : public ScriptEventListener_BASE
86 {
87 public:
88 	ScriptEventListenerWrapper( FmFormModel& _rModel) throw ( RuntimeException )
89 		:m_rModel( _rModel )
90 		,m_attemptedListenerCreation( false )
91 	{
92 
93 	}
94     // XEventListener
95     virtual void SAL_CALL disposing(const EventObject& ) throw( RuntimeException ){}
96 
97     // XScriptListener
98     virtual void SAL_CALL firing(const  ScriptEvent& evt) throw(RuntimeException)
99 	{
100 		attemptListenerCreation();
101 		if ( m_vbaListener.is() )
102 		{
103 			m_vbaListener->firing( evt );
104 		}
105 	}
106 
107     virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) throw( com::sun::star::reflection::InvocationTargetException, RuntimeException)
108 	{
109 		attemptListenerCreation();
110 		if ( m_vbaListener.is() )
111 		{
112 			return m_vbaListener->approveFiring( evt );
113 		}
114 		return Any();
115 	}
116 
117 private:
118 	void attemptListenerCreation()
119 	{
120 		if ( m_attemptedListenerCreation )
121 			return;
122 		m_attemptedListenerCreation = true;
123 
124 		try
125 		{
126 			::comphelper::ComponentContext const aContext( ::comphelper::getProcessServiceFactory() );
127 			Reference< XScriptListener > const xScriptListener( aContext.createComponent( "ooo.vba.EventListener" ), UNO_QUERY_THROW );
128 			Reference< XPropertySet > const xListenerProps( xScriptListener, UNO_QUERY_THROW );
129             // SfxObjectShellRef is good here since the model controls the lifetime of the shell
130 			SfxObjectShellRef const xObjectShell = m_rModel.GetObjectShell();
131 			ENSURE_OR_THROW( xObjectShell.Is(), "no object shell!" );
132 			xListenerProps->setPropertyValue( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Model" ) ), makeAny( xObjectShell->GetModel() ) );
133 
134 			m_vbaListener = xScriptListener;
135 		}
136 		catch( Exception const & )
137 		{
138 			DBG_UNHANDLED_EXCEPTION();
139 		}
140 	}
141     FmFormModel&					m_rModel;
142 	Reference< XScriptListener >	m_vbaListener;
143 	bool							m_attemptedListenerCreation;
144 
145 
146 };
147 
148 //------------------------------------------------------------------------------
149 // some helper structs for caching property infos
150 //------------------------------------------------------------------------------
151 struct PropertyInfo
152 {
153 	sal_Bool	bIsTransientOrReadOnly	: 1;	// the property is transient or read-only, thus we need no undo action for it
154 	sal_Bool	bIsValueProperty	    : 1;	// the property is the special value property, thus it may be handled
155 										    // as if it's transient or persistent
156 };
157 
158 struct PropertySetInfo
159 {
160 	DECLARE_STL_USTRINGACCESS_MAP(PropertyInfo, AllProperties);
161 
162 	AllProperties	aProps; 				// all properties of this set which we know so far
163 	sal_Bool			bHasEmptyControlSource; // sal_True -> the set has a DataField property, and the current value is an empty string
164 											// sal_False -> the set has _no_ such property or it's value isn't empty
165 };
166 
167 sal_Bool operator < (const Reference< XPropertySet >& lhs,
168 				 const Reference< XPropertySet >& rhs)
169 {
170 	return lhs.get() < rhs.get();
171 }
172 
173 DECLARE_STL_STDKEY_MAP(Reference< XPropertySet >, PropertySetInfo, PropertySetInfoCache);
174 
175 //------------------------------------------------------------------------------
176 
177 String static_STR_UNDO_PROPERTY;
178 //------------------------------------------------------------------------------
179 DBG_NAME(FmXUndoEnvironment)
180 //------------------------------------------------------------------------------
181 FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel& _rModel)
182 				   :rModel( _rModel )
183                    ,m_pPropertySetCache( NULL )
184                    ,m_pScriptingEnv( ::svxform::createDefaultFormScriptingEnvironment( _rModel ) )
185 				   ,m_Locks( 0 )
186 				   ,bReadOnly( sal_False )
187                    ,m_bDisposed( false )
188 {
189     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::FmXUndoEnvironment" );
190 	DBG_CTOR(FmXUndoEnvironment,NULL);
191 	try
192 	{
193 		m_vbaListener =  new ScriptEventListenerWrapper( _rModel );
194 	}
195 	catch( Exception& )
196 	{
197 	}
198 }
199 
200 //------------------------------------------------------------------------------
201 FmXUndoEnvironment::~FmXUndoEnvironment()
202 {
203 	DBG_DTOR(FmXUndoEnvironment,NULL);
204 	if (m_pPropertySetCache)
205 		delete static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
206 }
207 
208 //------------------------------------------------------------------------------
209 void FmXUndoEnvironment::dispose()
210 {
211     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::dispose" );
212     OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::dispose: disposed twice?" );
213     if ( !m_bDisposed )
214         return;
215 
216 	Lock();
217 
218     sal_uInt16 nCount = rModel.GetPageCount();
219 	sal_uInt16 i;
220 	for (i = 0; i < nCount; i++)
221 	{
222         FmFormPage* pPage = PTR_CAST( FmFormPage, rModel.GetPage(i) );
223         if ( pPage )
224         {
225             Reference< XInterface > xForms = pPage->GetForms( false ).get();
226             if ( xForms.is() )
227 		        RemoveElement( xForms );
228         }
229 	}
230 
231 	nCount = rModel.GetMasterPageCount();
232 	for (i = 0; i < nCount; i++)
233 	{
234         FmFormPage* pPage = PTR_CAST( FmFormPage, rModel.GetMasterPage(i) );
235         if ( pPage )
236         {
237             Reference< XInterface > xForms = pPage->GetForms( false ).get();
238             if ( xForms.is() )
239 		        RemoveElement( xForms );
240         }
241 	}
242 
243     UnLock();
244 
245     OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
246     if ( rModel.GetObjectShell() )
247 	    EndListening( *rModel.GetObjectShell() );
248 
249 	if ( IsListening( rModel ) )
250 		EndListening( rModel );
251 
252     m_pScriptingEnv->dispose();
253 
254     m_bDisposed = true;
255 }
256 
257 //------------------------------------------------------------------------------
258 void FmXUndoEnvironment::ModeChanged()
259 {
260     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::ModeChanged" );
261     OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
262     if ( !rModel.GetObjectShell() )
263         return;
264 
265 	if (bReadOnly != (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
266 	{
267 		bReadOnly = !bReadOnly;
268 
269 		sal_uInt16 nCount = rModel.GetPageCount();
270 		sal_uInt16 i;
271 		for (i = 0; i < nCount; i++)
272 		{
273             FmFormPage* pPage = PTR_CAST( FmFormPage, rModel.GetPage(i) );
274             if ( pPage )
275             {
276                 Reference< XInterface > xForms = pPage->GetForms( false ).get();
277                 if ( xForms.is() )
278                     TogglePropertyListening( xForms );
279             }
280 		}
281 
282 		nCount = rModel.GetMasterPageCount();
283 		for (i = 0; i < nCount; i++)
284 		{
285             FmFormPage* pPage = PTR_CAST( FmFormPage, rModel.GetMasterPage(i) );
286             if ( pPage )
287             {
288                 Reference< XInterface > xForms = pPage->GetForms( false ).get();
289                 if ( xForms.is() )
290 		            TogglePropertyListening( xForms );
291             }
292 		}
293 
294 		if (!bReadOnly)
295 			StartListening(rModel);
296 		else
297 			EndListening(rModel);
298 	}
299 }
300 
301 //------------------------------------------------------------------------------
302 void FmXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
303 {
304     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::Notify" );
305 	if (rHint.ISA(SdrHint))
306 	{
307 		SdrHint* pSdrHint = (SdrHint*)&rHint;
308 		switch( pSdrHint->GetKind() )
309 		{
310 			case HINT_OBJINSERTED:
311 			{
312 				SdrObject* pSdrObj = (SdrObject*)pSdrHint->GetObject();
313 				Inserted( pSdrObj );
314 			}	break;
315 			case HINT_OBJREMOVED:
316 			{
317 				SdrObject* pSdrObj = (SdrObject*)pSdrHint->GetObject();
318 				Removed( pSdrObj );
319 			}
320 			break;
321             default:
322                 break;
323 		}
324 	}
325 	else if (rHint.ISA(SfxSimpleHint))
326 	{
327 		switch ( ((SfxSimpleHint&)rHint).GetId() )
328 		{
329 			case SFX_HINT_DYING:
330 				dispose();
331                 rModel.SetObjectShell( NULL );
332 				break;
333 			case SFX_HINT_MODECHANGED:
334 				ModeChanged();
335 				break;
336 		}
337 	}
338 	else if (rHint.ISA(SfxEventHint))
339 	{
340 		switch (((SfxEventHint&)rHint).GetEventId())
341 		{
342 		case SFX_EVENT_CREATEDOC:
343 			case SFX_EVENT_OPENDOC:
344 				ModeChanged();
345 				break;
346 		}
347 	}
348 
349 }
350 
351 //------------------------------------------------------------------
352 void FmXUndoEnvironment::Inserted(SdrObject* pObj)
353 {
354     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::Inserted" );
355 	if (pObj->GetObjInventor() == FmFormInventor)
356 	{
357 		FmFormObj* pFormObj = PTR_CAST(FmFormObj, pObj);
358 		Inserted( pFormObj );
359 	}
360 	else if (pObj->IsGroupObject())
361 	{
362 		SdrObjListIter aIter(*pObj->GetSubList());
363 		while ( aIter.IsMore() )
364 			Inserted( aIter.Next() );
365 	}
366 }
367 
368 //------------------------------------------------------------------------------
369 namespace
370 {
371     sal_Bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
372     {
373 	    if (!xCont.is() || !xElement.is())
374 		    return sal_False;
375 
376 	    sal_Int32 nCount = xCont->getCount();
377 	    Reference< XInterface > xComp;
378 	    for (sal_Int32 i = 0; i < nCount; i++)
379 	    {
380 		    try
381 		    {
382 			    xCont->getByIndex(i) >>= xComp;
383 			    if (xComp.is())
384 			    {
385 				    if ( xElement == xComp )
386 					    return sal_True;
387 				    else
388 				    {
389 					    Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
390 					    if (xCont2.is() && lcl_searchElement(xCont2, xElement))
391 						    return sal_True;
392 				    }
393 			    }
394 		    }
395 		    catch(const Exception&)
396 		    {
397                 DBG_UNHANDLED_EXCEPTION();
398 		    }
399 	    }
400 	    return sal_False;
401     }
402 }
403 
404 //------------------------------------------------------------------------------
405 void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
406 {
407     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::Inserted" );
408 	DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
409 	if ( !pObj )
410 		return;
411 
412 	// ist das Control noch einer Form zugeordnet
413 	Reference< XInterface >  xModel(pObj->GetUnoControlModel(), UNO_QUERY);
414 	Reference< XFormComponent >  xContent(xModel, UNO_QUERY);
415 	if (xContent.is() && pObj->GetPage())
416 	{
417 		// if the component doesn't belong to a form, yet, find one to insert into
418 		if (!xContent->getParent().is())
419 		{
420             try
421             {
422 			    Reference< XIndexContainer > xObjectParent = pObj->GetOriginalParent();
423 
424                 FmFormPage& rPage = dynamic_cast< FmFormPage& >( *pObj->GetPage() );
425 			    Reference< XIndexAccess >  xForms( rPage.GetForms(), UNO_QUERY_THROW );
426 
427 			    Reference< XIndexContainer > xNewParent;
428                 Reference< XForm >           xForm;
429 			    sal_Int32 nPos = -1;
430 			    if ( lcl_searchElement( xForms, xObjectParent ) )
431                 {
432                     // the form which was the parent of the object when it was removed is still
433                     // part of the form component hierachy of the current page
434 				    xNewParent = xObjectParent;
435                     xForm.set( xNewParent, UNO_QUERY_THROW );
436                     nPos = ::std::min( pObj->GetOriginalIndex(), xNewParent->getCount() );
437                 }
438 			    else
439 			    {
440 				    xForm.set( rPage.GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
441 				    xNewParent.set( xForm, UNO_QUERY_THROW );
442                     nPos = xNewParent->getCount();
443 			    }
444 
445                 rPage.GetImpl().setUniqueName( xContent, xForm );
446 			    xNewParent->insertByIndex( nPos, makeAny( xContent ) );
447 
448                 Reference< XEventAttacherManager >	xManager( xNewParent, UNO_QUERY_THROW );
449 			    xManager->registerScriptEvents( nPos, pObj->GetOriginalEvents() );
450             }
451             catch( const Exception& )
452             {
453             	DBG_UNHANDLED_EXCEPTION();
454             }
455 		}
456 
457 		// FormObject zuruecksetzen
458 		pObj->ClearObjEnv();
459 	}
460 }
461 
462 //------------------------------------------------------------------
463 void FmXUndoEnvironment::Removed(SdrObject* pObj)
464 {
465     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::Removed" );
466     if ( pObj->IsVirtualObj() )
467         // for virtual objects, we've already been notified of the removal of the master
468         // object, which is sufficient here
469         return;
470 
471 	if (pObj->GetObjInventor() == FmFormInventor)
472 	{
473 		FmFormObj* pFormObj = PTR_CAST(FmFormObj, pObj);
474 		Removed(pFormObj);
475 	}
476 	else if (pObj->IsGroupObject())
477 	{
478 		SdrObjListIter aIter(*pObj->GetSubList());
479 		while ( aIter.IsMore() )
480 			Removed( aIter.Next() );
481 	}
482 }
483 
484 //------------------------------------------------------------------------------
485 void FmXUndoEnvironment::Removed(FmFormObj* pObj)
486 {
487     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::Removed" );
488 	DBG_ASSERT( pObj, "FmXUndoEnvironment::Removed: invalid object!" );
489 	if ( !pObj )
490 		return;
491 
492 	// ist das Control noch einer Form zugeordnet
493 	Reference< XFormComponent >  xContent(pObj->GetUnoControlModel(), UNO_QUERY);
494 	if (xContent.is())
495 	{
496 		// das Object wird aus einer Liste herausgenommen
497 		// existiert ein Vater wird das Object beim beim Vater entfernt und
498 		// am FormObject gemerkt!
499 
500 		// wird das Object wieder eingefuegt und ein Parent existiert, so wird dieser
501 		// Parent wiederum gesetzt
502 		Reference< XIndexContainer >  xForm(xContent->getParent(), UNO_QUERY);
503 		if (xForm.is())
504 		{
505 			Reference< XIndexAccess >  xIndexAccess((XIndexContainer*)xForm.get());
506 			// Feststellen an welcher Position sich das Kind befunden hat
507 			const sal_Int32 nPos = getElementPos(xIndexAccess, xContent);
508 			if (nPos >= 0)
509 			{
510 				Sequence< ScriptEventDescriptor > aEvts;
511 				Reference< XEventAttacherManager >	xManager(xForm, UNO_QUERY);
512 				if (xManager.is())
513 					aEvts = xManager->getScriptEvents(nPos);
514 
515 				try
516 				{
517 					pObj->SetObjEnv(xForm, nPos, aEvts);
518 					xForm->removeByIndex(nPos);
519 				}
520 				catch(Exception&)
521 				{
522                     DBG_UNHANDLED_EXCEPTION();
523 				}
524 
525 			}
526 		}
527 	}
528 }
529 
530 //	XEventListener
531 //------------------------------------------------------------------------------
532 void SAL_CALL FmXUndoEnvironment::disposing(const EventObject& e) throw( RuntimeException )
533 {
534     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::disposing" );
535 	// check if it's an object we have cached informations about
536 	if (m_pPropertySetCache)
537 	{
538 		Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
539 		if (xSourceSet.is())
540 		{
541 			PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
542 			PropertySetInfoCacheIterator aSetPos = pCache->find(xSourceSet);
543 			if (aSetPos != pCache->end())
544 				pCache->erase(aSetPos);
545 		}
546 	}
547 }
548 
549 // XPropertyChangeListener
550 //------------------------------------------------------------------------------
551 void SAL_CALL FmXUndoEnvironment::propertyChange(const PropertyChangeEvent& evt) throw(::com::sun::star::uno::RuntimeException)
552 {
553     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::propertyChange" );
554     ::osl::ClearableMutexGuard aGuard( m_aMutex );
555 
556 	if (!IsLocked())
557 	{
558 		Reference< XPropertySet >  xSet(evt.Source, UNO_QUERY);
559 		if (!xSet.is())
560 			return;
561 
562 		// if it's a "default value" property of a control model, set the according "value" property
563 		static ::rtl::OUString pDefaultValueProperties[] = {
564 			FM_PROP_DEFAULT_TEXT, FM_PROP_DEFAULTCHECKED, FM_PROP_DEFAULT_DATE, FM_PROP_DEFAULT_TIME,
565 			FM_PROP_DEFAULT_VALUE, FM_PROP_DEFAULT_SELECT_SEQ, FM_PROP_EFFECTIVE_DEFAULT
566 		};
567 		const ::rtl::OUString aValueProperties[] = {
568 			FM_PROP_TEXT, FM_PROP_STATE, FM_PROP_DATE, FM_PROP_TIME,
569 			FM_PROP_VALUE, FM_PROP_SELECT_SEQ, FM_PROP_EFFECTIVE_VALUE
570 		};
571 		sal_Int32 nDefaultValueProps = sizeof(pDefaultValueProperties)/sizeof(pDefaultValueProperties[0]);
572 		OSL_ENSURE(sizeof(aValueProperties)/sizeof(aValueProperties[0]) == nDefaultValueProps,
573 			"FmXUndoEnvironment::propertyChange: inconsistence!");
574 		for (sal_Int32 i=0; i<nDefaultValueProps; ++i)
575 		{
576 			if (0 == evt.PropertyName.compareTo(pDefaultValueProperties[i]))
577 			{
578 				try
579 				{
580 					xSet->setPropertyValue(aValueProperties[i], evt.NewValue);
581 				}
582 				catch(const Exception&)
583 				{
584 					OSL_ENSURE(sal_False, "FmXUndoEnvironment::propertyChange: could not adjust the value property!");
585 				}
586 			}
587 		}
588 
589 		// no Undo for transient and readonly props. But unfortunately "transient" is not only that the
590 		// "transient" flag is set for the property in question, instead is is somewhat more complex
591         // Transience criterions are:
592         // - the "transient" flag is set for the property
593         // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
594         //   to a database column. Note that it doesn't matter here whether the control actually
595         //   *is* bound to a column
596         // - OR the control is bound to an external value via XBindableValue/XValueBinding
597         //   which does not have a "ExternalData" property being <TRUE/>
598 
599 		if (!m_pPropertySetCache)
600 			m_pPropertySetCache = new PropertySetInfoCache;
601 		PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
602 
603 		// let's see if we know something about the set
604 		PropertySetInfoCacheIterator aSetPos = pCache->find(xSet);
605 		if (aSetPos == pCache->end())
606 		{
607 			PropertySetInfo aNewEntry;
608 			if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xSet))
609 			{
610 				aNewEntry.bHasEmptyControlSource = sal_False;
611 			}
612 			else
613 			{
614 				try
615 				{
616 					Any aCurrentControlSource = xSet->getPropertyValue(FM_PROP_CONTROLSOURCE);
617 					aNewEntry.bHasEmptyControlSource = !aCurrentControlSource.hasValue() || (::comphelper::getString(aCurrentControlSource).getLength() == 0);
618 				}
619 				catch(const Exception&)
620 				{
621                     DBG_UNHANDLED_EXCEPTION();
622 				}
623 			}
624 			aSetPos = pCache->insert(PropertySetInfoCache::value_type(xSet,aNewEntry)).first;
625 			DBG_ASSERT(aSetPos != pCache->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
626 		}
627 		else
628 		{	// is it the DataField property ?
629 			if (evt.PropertyName.equals(FM_PROP_CONTROLSOURCE))
630 			{
631 				aSetPos->second.bHasEmptyControlSource = !evt.NewValue.hasValue() || (::comphelper::getString(evt.NewValue).getLength() == 0);
632 			}
633 		}
634 
635 		// now we have access to the cached info about the set
636 		// let's see what we know about the property
637 		PropertySetInfo::AllProperties& rPropInfos = aSetPos->second.aProps;
638 		PropertySetInfo::AllPropertiesIterator aPropertyPos = rPropInfos.find(evt.PropertyName);
639 		if (aPropertyPos == rPropInfos.end())
640 		{	// nothing 'til now ... have to change this ....
641 			PropertyInfo aNewEntry;
642 
643 			// the attributes
644 			sal_Int32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName(evt.PropertyName).Attributes;
645 			aNewEntry.bIsTransientOrReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
646 
647 			// check if it is the special "DataFieldProperty"
648 			aNewEntry.bIsValueProperty = sal_False;
649 			try
650 			{
651 				if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY, xSet))
652 				{
653 					Any aControlSourceProperty = xSet->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY);
654 					::rtl::OUString sControlSourceProperty;
655 					aControlSourceProperty >>= sControlSourceProperty;
656 
657 					aNewEntry.bIsValueProperty = (sControlSourceProperty.equals(evt.PropertyName));
658 				}
659 			}
660 			catch(const Exception&)
661 			{
662                 DBG_UNHANDLED_EXCEPTION();
663 			}
664 
665 			// insert the new entry
666 			aPropertyPos = rPropInfos.insert(PropertySetInfo::AllProperties::value_type(evt.PropertyName,aNewEntry)).first;
667 			DBG_ASSERT(aPropertyPos != rPropInfos.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
668 		}
669 
670 		// now we have access to the cached info about the property affected
671 		// and are able to decide wether or not we need an undo action
672 
673         bool bAddUndoAction = rModel.IsUndoEnabled();
674         // no UNDO for transient/readonly properties
675         if ( bAddUndoAction && aPropertyPos->second.bIsTransientOrReadOnly )
676             bAddUndoAction = false;
677 
678 		if ( bAddUndoAction && aPropertyPos->second.bIsValueProperty )
679 		{
680             // no UNDO when the "value" property changes, but the ControlSource is non-empty
681             // (in this case the control is intended to be bound to a database column)
682             if ( !aSetPos->second.bHasEmptyControlSource )
683                 bAddUndoAction = false;
684 
685             // no UNDO if the control is currently bound to an external value
686             if ( bAddUndoAction )
687             {
688                 Reference< XBindableValue > xBindable( evt.Source, UNO_QUERY );
689                 Reference< XValueBinding > xBinding;
690                 if ( xBindable.is() )
691                     xBinding = xBindable->getValueBinding();
692 
693                 Reference< XPropertySet > xBindingProps;
694                 Reference< XPropertySetInfo > xBindingPropsPSI;
695                 if ( xBindable.is() )
696                     xBindingProps.set( xBinding, UNO_QUERY );
697                 if ( xBindingProps.is() )
698                     xBindingPropsPSI = xBindingProps->getPropertySetInfo();
699                 // TODO: we should cache all those things, else this might be too expensive.
700                 // However, this requires we're notified of changes in the value binding
701 
702                 static const ::rtl::OUString s_sExternalData( RTL_CONSTASCII_USTRINGPARAM( "ExternalData" ) );
703                 if ( xBindingPropsPSI.is() && xBindingPropsPSI->hasPropertyByName( s_sExternalData ) )
704                 {
705                     sal_Bool bExternalData = sal_True;
706                     OSL_VERIFY( xBindingProps->getPropertyValue( s_sExternalData ) >>= bExternalData );
707                     bAddUndoAction = !bExternalData;
708                 }
709                 else
710                     bAddUndoAction = !xBinding.is();
711             }
712         }
713 
714         if ( bAddUndoAction && ( evt.PropertyName == FM_PROP_STRINGITEMLIST ) )
715         {
716             Reference< XListEntrySink > xSink( evt.Source, UNO_QUERY );
717             if ( xSink.is() && xSink->getListEntrySource().is() )
718                 // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
719                 bAddUndoAction = false;
720         }
721 
722         if ( bAddUndoAction )
723         {
724             aGuard.clear();
725             // TODO: this is a potential race condition: two threads here could in theory
726             // add their undo actions out-of-order
727 
728             ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
729             rModel.AddUndo(new FmUndoPropertyAction(rModel, evt));
730         }
731 	}
732 	else
733 	{
734 		// if it's the DataField property we may have to adjust our cache
735 		if (m_pPropertySetCache && evt.PropertyName.equals(FM_PROP_CONTROLSOURCE))
736 		{
737 			Reference< XPropertySet >  xSet(evt.Source, UNO_QUERY);
738 			PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
739 			PropertySetInfo& rSetInfo = (*pCache)[xSet];
740 			rSetInfo.bHasEmptyControlSource = !evt.NewValue.hasValue() || (::comphelper::getString(evt.NewValue).getLength() == 0);
741 		}
742 	}
743 }
744 
745 // XContainerListener
746 //------------------------------------------------------------------------------
747 void SAL_CALL FmXUndoEnvironment::elementInserted(const ContainerEvent& evt) throw(::com::sun::star::uno::RuntimeException)
748 {
749     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::elementInserted" );
750 	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
751     ::osl::MutexGuard aGuard( m_aMutex );
752 
753 	// neues Object zum lauschen
754 	Reference< XInterface >  xIface;
755 	evt.Element >>= xIface;
756 	OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
757 	AddElement(xIface);
758 
759     implSetModified();
760 }
761 
762 //------------------------------------------------------------------------------
763 void FmXUndoEnvironment::implSetModified()
764 {
765     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::implSetModified" );
766 	if ( !IsLocked() && rModel.GetObjectShell() )
767 	{
768 		rModel.GetObjectShell()->SetModified( sal_True );
769 	}
770 }
771 
772 //------------------------------------------------------------------------------
773 void SAL_CALL FmXUndoEnvironment::elementReplaced(const ContainerEvent& evt) throw(::com::sun::star::uno::RuntimeException)
774 {
775     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::elementReplaced" );
776 	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
777     ::osl::MutexGuard aGuard( m_aMutex );
778 
779     Reference< XInterface >  xIface;
780 	evt.ReplacedElement >>= xIface;
781 	OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
782 	RemoveElement(xIface);
783 
784 	evt.Element >>= xIface;
785 	AddElement(xIface);
786 
787     implSetModified();
788 }
789 
790 //------------------------------------------------------------------------------
791 void SAL_CALL FmXUndoEnvironment::elementRemoved(const ContainerEvent& evt) throw(::com::sun::star::uno::RuntimeException)
792 {
793     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::elementRemoved" );
794 	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
795     ::osl::MutexGuard aGuard( m_aMutex );
796 
797     Reference< XInterface >  xIface( evt.Element, UNO_QUERY );
798 	OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
799 	RemoveElement(xIface);
800 
801     implSetModified();
802 }
803 
804 //------------------------------------------------------------------------------
805 void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ ) throw (RuntimeException)
806 {
807     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::modified" );
808     implSetModified();
809 }
810 
811 //------------------------------------------------------------------------------
812 void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
813 {
814     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::AddForms" );
815 	Lock();
816 	Reference< XInterface >  xInt(rForms, UNO_QUERY);
817 	AddElement(xInt);
818 	UnLock();
819 }
820 
821 //------------------------------------------------------------------------------
822 void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
823 {
824     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::RemoveForms" );
825 	Lock();
826 	Reference< XInterface >  xInt(rForms, UNO_QUERY);
827 	RemoveElement(xInt);
828 	UnLock();
829 }
830 
831 //------------------------------------------------------------------------------
832 void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
833 {
834     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::TogglePropertyListening" );
835 	// am Container horchen
836 	Reference< XIndexContainer >  xContainer(Element, UNO_QUERY);
837 	if (xContainer.is())
838 	{
839 		sal_uInt32 nCount = xContainer->getCount();
840 		Reference< XInterface >  xIface;
841 		for (sal_uInt32 i = 0; i < nCount; i++)
842 		{
843 			xContainer->getByIndex(i) >>= xIface;
844 			TogglePropertyListening(xIface);
845 		}
846 	}
847 
848 	Reference< XPropertySet >  xSet(Element, UNO_QUERY);
849 	if (xSet.is())
850 	{
851 		if (!bReadOnly)
852 			xSet->addPropertyChangeListener( ::rtl::OUString(), this );
853 		else
854 			xSet->removePropertyChangeListener( ::rtl::OUString(), this );
855 	}
856 }
857 
858 
859 //------------------------------------------------------------------------------
860 void FmXUndoEnvironment::switchListening( const Reference< XIndexContainer >& _rxContainer, bool _bStartListening ) SAL_THROW(())
861 {
862     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::switchListening" );
863     OSL_PRECOND( _rxContainer.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
864     if ( !_rxContainer.is() )
865         return;
866 
867     try
868     {
869 	    // if it's an EventAttacherManager, then we need to listen for
870         // script events
871 	    Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY );
872 	    if ( xManager.is() )
873 	    {
874             if ( _bStartListening )
875             {
876 		        m_pScriptingEnv->registerEventAttacherManager( xManager );
877                 if ( m_vbaListener.is() )
878                     xManager->addScriptListener( m_vbaListener );
879             }
880             else
881             {
882 		        m_pScriptingEnv->revokeEventAttacherManager( xManager );
883                 if ( m_vbaListener.is() )
884                     xManager->removeScriptListener( m_vbaListener );
885             }
886 		}
887 
888         // also handle all children of this element
889 	    sal_uInt32 nCount = _rxContainer->getCount();
890 	    Reference< XInterface > xInterface;
891 	    for ( sal_uInt32 i = 0; i < nCount; ++i )
892 	    {
893 		    _rxContainer->getByIndex( i ) >>= xInterface;
894             if ( _bStartListening )
895 		        AddElement( xInterface );
896             else
897 		        RemoveElement( xInterface );
898 	    }
899 
900         // be notified of any changes in the container elements
901 	    Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
902         OSL_ENSURE( xSimpleContainer.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
903 	    if ( xSimpleContainer.is() )
904 	    {
905             if ( _bStartListening )
906 		        xSimpleContainer->addContainerListener( this );
907             else
908 		        xSimpleContainer->removeContainerListener( this );
909 	    }
910     }
911     catch( const Exception& )
912     {
913     	OSL_ENSURE( sal_False, "FmXUndoEnvironment::switchListening: caught an exception!" );
914     }
915 }
916 
917 //------------------------------------------------------------------------------
918 void FmXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening ) SAL_THROW(())
919 {
920     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::switchListening" );
921     OSL_PRECOND( _rxObject.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
922 
923     try
924     {
925         if ( !bReadOnly )
926         {
927             Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
928 	        if ( xProps.is() )
929 	        {
930                 if ( _bStartListening )
931     		        xProps->addPropertyChangeListener( ::rtl::OUString(), this );
932                 else
933     	    	    xProps->removePropertyChangeListener( ::rtl::OUString(), this );
934 	        }
935         }
936 
937         Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
938         if ( xBroadcaster.is() )
939         {
940             if ( _bStartListening )
941                 xBroadcaster->addModifyListener( this );
942             else
943                 xBroadcaster->removeModifyListener( this );
944         }
945     }
946     catch( const Exception& )
947     {
948     	OSL_ENSURE( sal_False, "FmXUndoEnvironment::switchListening: caught an exception!" );
949     }
950 }
951 
952 //------------------------------------------------------------------------------
953 void FmXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
954 {
955     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::AddElement" );
956     OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
957 
958     // am Container horchen
959 	Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
960 	if ( xContainer.is() )
961         switchListening( xContainer, true );
962 
963     switchListening( _rxElement, true );
964 }
965 
966 //------------------------------------------------------------------------------
967 void FmXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
968 {
969     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmXUndoEnvironment::RemoveElement" );
970     if ( m_bDisposed )
971         return;
972 
973     switchListening( _rxElement, false );
974 
975 	if (!bReadOnly)
976     {
977 		// reset the ActiveConnection if the form is to be removed. This will (should) free the resources
978 		// associated with this connection
979 		// 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
980         Reference< XForm > xForm( _rxElement, UNO_QUERY );
981         Reference< XPropertySet > xFormProperties( xForm, UNO_QUERY );
982 		if ( xFormProperties.is() )
983             if ( !::svxform::OStaticDataAccessTools().isEmbeddedInDatabase( _rxElement ) )
984                 // (if there is a connection in the context of the component, setting
985                 // a new connection would be vetoed, anyway)
986                 // #i34196# - 2004-09-21 - fs@openoffice.org
987 			    xFormProperties->setPropertyValue( FM_PROP_ACTIVE_CONNECTION, Any() );
988 	}
989 
990 	Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
991     if ( xContainer.is() )
992         switchListening( xContainer, false );
993 }
994 
995 
996 //------------------------------------------------------------------------------
997 FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel& rNewMod, const PropertyChangeEvent& evt)
998 					 :SdrUndoAction(rNewMod)
999 					 ,xObj(evt.Source, UNO_QUERY)
1000 					 ,aPropertyName(evt.PropertyName)
1001 					 ,aNewValue(evt.NewValue)
1002 					 ,aOldValue(evt.OldValue)
1003 {
1004     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoPropertyAction::FmUndoPropertyAction" );
1005 	if (rNewMod.GetObjectShell())
1006 		rNewMod.GetObjectShell()->SetModified(sal_True);
1007 	if(static_STR_UNDO_PROPERTY.Len() == 0)
1008 		static_STR_UNDO_PROPERTY = SVX_RES(RID_STR_UNDO_PROPERTY);
1009 }
1010 
1011 
1012 //------------------------------------------------------------------------------
1013 void FmUndoPropertyAction::Undo()
1014 {
1015     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoPropertyAction::Undo" );
1016 	FmXUndoEnvironment& rEnv = ((FmFormModel&)rMod).GetUndoEnv();
1017 
1018 	if (xObj.is() && !rEnv.IsLocked())
1019 	{
1020 		rEnv.Lock();
1021         try
1022         {
1023 		    xObj->setPropertyValue( aPropertyName, aOldValue );
1024         }
1025         catch( const Exception& )
1026         {
1027         	OSL_ENSURE( sal_False, "FmUndoPropertyAction::Undo: caught an exception!" );
1028         }
1029 		rEnv.UnLock();
1030 	}
1031 }
1032 
1033 //------------------------------------------------------------------------------
1034 void FmUndoPropertyAction::Redo()
1035 {
1036     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoPropertyAction::Redo" );
1037 	FmXUndoEnvironment& rEnv = ((FmFormModel&)rMod).GetUndoEnv();
1038 
1039 	if (xObj.is() && !rEnv.IsLocked())
1040 	{
1041 		rEnv.Lock();
1042         try
1043         {
1044 		    xObj->setPropertyValue( aPropertyName, aNewValue );
1045         }
1046         catch( const Exception& )
1047         {
1048         	OSL_ENSURE( sal_False, "FmUndoPropertyAction::Redo: caught an exception!" );
1049         }
1050 		rEnv.UnLock();
1051 	}
1052 }
1053 
1054 //------------------------------------------------------------------------------
1055 String FmUndoPropertyAction::GetComment() const
1056 {
1057     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoPropertyAction::GetComment" );
1058 	String aStr(static_STR_UNDO_PROPERTY);
1059 
1060 	aStr.SearchAndReplace( '#', aPropertyName );
1061 	return aStr;
1062 }
1063 
1064 
1065 DBG_NAME(FmUndoContainerAction);
1066 //------------------------------------------------------------------------------
1067 FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
1068 											 Action _eAction,
1069 											 const Reference< XIndexContainer > & xCont,
1070 											 const Reference< XInterface > & xElem,
1071 											 sal_Int32 nIdx)
1072 					  :SdrUndoAction( _rMod )
1073 					  ,m_xContainer( xCont )
1074 					  ,m_nIndex( nIdx )
1075 					  ,m_eAction( _eAction )
1076 {
1077     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoContainerAction::FmUndoContainerAction" );
1078     OSL_ENSURE( nIdx >= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
1079         // some old code suggested this could be a valid argument. However, this code was
1080         // buggy, and it *seemed* that nobody used it - so it was removed.
1081 
1082 	DBG_CTOR(FmUndoContainerAction,NULL);
1083 	if ( xCont.is() && xElem.is() )
1084 	{
1085 		// normalize
1086         m_xElement = m_xElement.query( xElem );
1087         if ( m_eAction == Removed )
1088         {
1089 			if (m_nIndex >= 0)
1090 			{
1091 				Reference< XEventAttacherManager >	xManager( xCont, UNO_QUERY );
1092 				if ( xManager.is() )
1093 					m_aEvents = xManager->getScriptEvents(m_nIndex);
1094 			}
1095 			else
1096 				m_xElement = NULL;
1097 
1098             // we now own the element
1099 			m_xOwnElement = m_xElement;
1100         }
1101 	}
1102 }
1103 
1104 //------------------------------------------------------------------------------
1105 FmUndoContainerAction::~FmUndoContainerAction()
1106 {
1107     // if we own the object ....
1108 	DisposeElement( m_xOwnElement );
1109 	DBG_DTOR(FmUndoContainerAction,NULL);
1110 }
1111 
1112 //------------------------------------------------------------------------------
1113 
1114 void FmUndoContainerAction::DisposeElement( const Reference< XInterface > & xElem )
1115 {
1116 	Reference< XComponent > xComp( xElem, UNO_QUERY );
1117 	if ( xComp.is() )
1118 	{
1119         // and the object does not have a parent
1120 		Reference< XChild >  xChild( xElem, UNO_QUERY );
1121 		if ( xChild.is() && !xChild->getParent().is() )
1122             // -> dispose it
1123 			xComp->dispose();
1124 	}
1125 }
1126 
1127 //------------------------------------------------------------------------------
1128 void FmUndoContainerAction::implReInsert( ) SAL_THROW( ( Exception ) )
1129 {
1130     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoContainerAction::implReInsert" );
1131 	if ( m_xContainer->getCount() >= m_nIndex )
1132 	{
1133         // insert the element
1134 		Any aVal;
1135 		if ( m_xContainer->getElementType() == ::getCppuType( static_cast< const Reference< XFormComponent >* >( NULL ) ) )
1136 		{
1137 			aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
1138 		}
1139 		else
1140 		{
1141 			aVal <<= Reference< XForm >( m_xElement, UNO_QUERY );
1142 		}
1143 		m_xContainer->insertByIndex( m_nIndex, aVal );
1144 
1145         OSL_ENSURE( getElementPos( m_xContainer.get(), m_xElement ) == m_nIndex, "FmUndoContainerAction::implReInsert: insertion did not work!" );
1146 
1147         // register the events
1148         Reference< XEventAttacherManager >	xManager( m_xContainer, UNO_QUERY );
1149 		if ( xManager.is() )
1150 			xManager->registerScriptEvents( m_nIndex, m_aEvents );
1151 
1152         // we don't own the object anymore
1153 		m_xOwnElement = NULL;
1154 	}
1155 }
1156 
1157 //------------------------------------------------------------------------------
1158 void FmUndoContainerAction::implReRemove( ) SAL_THROW( ( Exception ) )
1159 {
1160     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoContainerAction::implReRemove" );
1161     Reference< XInterface > xElement;
1162     if ( ( m_nIndex >= 0 ) && ( m_nIndex < m_xContainer->getCount() ) )
1163     	m_xContainer->getByIndex( m_nIndex ) >>= xElement;
1164 
1165 	if ( xElement != m_xElement )
1166     {
1167         // the indexes in the container changed. Okay, so go the long way and
1168         // manually determine the index
1169         m_nIndex = getElementPos( m_xContainer.get(), m_xElement );
1170         if ( m_nIndex != -1 )
1171             xElement = m_xElement;
1172     }
1173 
1174     OSL_ENSURE( xElement == m_xElement, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
1175     if ( xElement == m_xElement )
1176 	{
1177 		Reference< XEventAttacherManager >	xManager( m_xContainer, UNO_QUERY );
1178 		if ( xManager.is() )
1179 			m_aEvents = xManager->getScriptEvents( m_nIndex );
1180 		m_xContainer->removeByIndex( m_nIndex );
1181         // from now on, we own this object
1182 		m_xOwnElement = m_xElement;
1183 	}
1184 }
1185 
1186 //------------------------------------------------------------------------------
1187 void FmUndoContainerAction::Undo()
1188 {
1189     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoContainerAction::Undo" );
1190 	FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
1191 
1192 	if ( m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is() )
1193 	{
1194 		rEnv.Lock();
1195         try
1196         {
1197 		    switch ( m_eAction )
1198 		    {
1199 			case Inserted:
1200                 implReRemove();
1201                 break;
1202 
1203 			case Removed:
1204                 implReInsert();
1205                 break;
1206     	    }
1207         }
1208         catch( const Exception& )
1209         {
1210         	OSL_ENSURE( sal_False, "FmUndoContainerAction::Undo: caught an exception!" );
1211         }
1212 		rEnv.UnLock();
1213 	}
1214 }
1215 
1216 //------------------------------------------------------------------------------
1217 void FmUndoContainerAction::Redo()
1218 {
1219     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoContainerAction::Redo" );
1220 	FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
1221 	if ( m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is() )
1222 	{
1223 		rEnv.Lock();
1224         try
1225         {
1226 	    	switch ( m_eAction )
1227 		    {
1228 			case Inserted:
1229                 implReInsert();
1230                 break;
1231 
1232 			case Removed:
1233                 implReRemove();
1234 			    break;
1235     		}
1236         }
1237         catch( const Exception& )
1238         {
1239         	OSL_ENSURE( sal_False, "FmUndoContainerAction::Redo: caught an exception!" );
1240         }
1241 		rEnv.UnLock();
1242 	}
1243 }
1244 
1245 //------------------------------------------------------------------------------
1246 FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel& _rMod, SdrUnoObj* _pObject, const Reference< XControlModel > & _xReplaced)
1247 	:SdrUndoAction(_rMod)
1248 	,m_xReplaced(_xReplaced)
1249 	,m_pObject(_pObject)
1250 {
1251     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoModelReplaceAction::FmUndoModelReplaceAction" );
1252 }
1253 
1254 //------------------------------------------------------------------------------
1255 FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
1256 {
1257 	// dispose our element if nobody else is responsible for
1258 	DisposeElement(m_xReplaced);
1259 }
1260 
1261 //------------------------------------------------------------------------------
1262 
1263 void FmUndoModelReplaceAction::DisposeElement( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XControlModel>& xReplaced )
1264 {
1265 	Reference< XComponent >  xComp(xReplaced, UNO_QUERY);
1266 	if (xComp.is())
1267 	{
1268 		Reference< XChild >  xChild(xReplaced, UNO_QUERY);
1269 		if (!xChild.is() || !xChild->getParent().is())
1270 			xComp->dispose();
1271 	}
1272 }
1273 
1274 //------------------------------------------------------------------------------
1275 void FmUndoModelReplaceAction::Undo()
1276 {
1277     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoModelReplaceAction::Undo" );
1278 	try
1279 	{
1280 		Reference< XControlModel > xCurrentModel( m_pObject->GetUnoControlModel() );
1281 
1282 		// replace the model within the parent
1283 		Reference< XChild > xCurrentAsChild( xCurrentModel, UNO_QUERY );
1284 		Reference< XNameContainer > xCurrentsParent;
1285 		if ( xCurrentAsChild.is() )
1286 			xCurrentsParent = xCurrentsParent.query( xCurrentAsChild->getParent() );
1287 		DBG_ASSERT( xCurrentsParent.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
1288 
1289 		if ( xCurrentsParent.is() )
1290 		{
1291 			// the form container works with FormComponents
1292 			Reference< XFormComponent > xComponent( m_xReplaced, UNO_QUERY );
1293 			DBG_ASSERT( xComponent.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
1294 
1295 			Reference< XPropertySet > xCurrentAsSet( xCurrentModel, UNO_QUERY );
1296 			DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME, xCurrentAsSet ), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
1297 
1298 			::rtl::OUString sName;
1299 			xCurrentAsSet->getPropertyValue( FM_PROP_NAME ) >>= sName;
1300 			xCurrentsParent->replaceByName( sName, makeAny( xComponent ) );
1301 
1302 			m_pObject->SetUnoControlModel(m_xReplaced);
1303 			m_pObject->SetChanged();
1304 
1305 			m_xReplaced = xCurrentModel;
1306 		}
1307 	}
1308 	catch(Exception&)
1309 	{
1310 		DBG_ERROR("FmUndoModelReplaceAction::Undo : could not replace the model !");
1311 	}
1312 }
1313 
1314 //------------------------------------------------------------------------------
1315 String FmUndoModelReplaceAction::GetComment() const
1316 {
1317     RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "svx", "Ocke.Janssen@sun.com", "FmUndoModelReplaceAction::GetComment" );
1318 	return SVX_RES(RID_STR_UNDO_MODEL_REPLACE);
1319 }
1320