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