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