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 #include "precompiled_reportdesign.hxx"
24 #include "UndoActions.hxx"
25 #include "UndoEnv.hxx"
26 #include "formatnormalizer.hxx"
27 #include "conditionupdater.hxx"
28 #include "corestrings.hrc"
29 #include "rptui_slotid.hrc"
30 #include "RptDef.hxx"
31 #include "ModuleHelper.hxx"
32 #include "RptObject.hxx"
33 #include "RptPage.hxx"
34 #include "RptResId.hrc"
35 #include "RptModel.hxx"
36 
37 /** === begin UNO includes === **/
38 #include <com/sun/star/script/XEventAttacherManager.hpp>
39 #include <com/sun/star/container/XChild.hpp>
40 #include <com/sun/star/container/XNameContainer.hpp>
41 #include <com/sun/star/beans/PropertyAttribute.hpp>
42 #include <com/sun/star/util/XModifyBroadcaster.hpp>
43 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
44 #include <com/sun/star/beans/XIntrospection.hpp>
45 /** === end UNO includes === **/
46 
47 #include <connectivity/dbtools.hxx>
48 #include <svl/smplhint.hxx>
49 #include <tools/diagnose_ex.h>
50 #include <comphelper/stl_types.hxx>
51 #include <comphelper/componentcontext.hxx>
52 #include <vcl/svapp.hxx>
53 #include <dbaccess/dbsubcomponentcontroller.hxx>
54 #include <svx/unoshape.hxx>
55 #include <vos/mutex.hxx>
56 
57 namespace rptui
58 {
59 	using namespace ::com::sun::star;
60 	using namespace uno;
61 	using namespace lang;
62 	using namespace script;
63 	using namespace beans;
64 	using namespace awt;
65 	using namespace util;
66 	using namespace container;
67 	using namespace report;
68 //----------------------------------------------------------------------------
69 
70 
71 struct PropertyInfo
72 {
73     bool    bIsReadonlyOrTransient;
74 
PropertyInforptui::PropertyInfo75     PropertyInfo()
76         :bIsReadonlyOrTransient( false )
77     {
78     }
79 
PropertyInforptui::PropertyInfo80     PropertyInfo( const bool i_bIsTransientOrReadOnly )
81         :bIsReadonlyOrTransient( i_bIsTransientOrReadOnly )
82     {
83     }
84 };
85 
86 typedef ::std::hash_map< ::rtl::OUString, PropertyInfo, ::rtl::OUStringHash >    PropertiesInfo;
87 
88 struct ObjectInfo
89 {
90     PropertiesInfo              aProperties;
91     Reference< XPropertySet >   xPropertyIntrospection;
92 
ObjectInforptui::ObjectInfo93     ObjectInfo()
94         :aProperties()
95         ,xPropertyIntrospection()
96     {
97     }
98 };
99 
100 typedef ::std::map< Reference< XPropertySet >, ObjectInfo, ::comphelper::OInterfaceCompare< XPropertySet > >    PropertySetInfoCache;
101 
102 // -----------------------------------------------------------------------------
103 
104 class OXUndoEnvironmentImpl
105 {
106     OXUndoEnvironmentImpl(OXUndoEnvironmentImpl&);
107     void operator =(OXUndoEnvironmentImpl&);
108 public:
109     OReportModel&                                       m_rModel;
110     PropertySetInfoCache                                m_aPropertySetCache;
111     FormatNormalizer                                    m_aFormatNormalizer;
112     ConditionUpdater                                    m_aConditionUpdater;
113     ::osl::Mutex                                        m_aMutex;
114 	::std::vector< uno::Reference< container::XChild> > m_aSections;
115     Reference< XIntrospection >                         m_xIntrospection;
116     oslInterlockedCount                                 m_nLocks;
117 	sal_Bool	                                        m_bReadOnly;
118     sal_Bool                                            m_bIsUndo;
119 
120     OXUndoEnvironmentImpl(OReportModel& _rModel);
121 };
122 
OXUndoEnvironmentImpl(OReportModel & _rModel)123 OXUndoEnvironmentImpl::OXUndoEnvironmentImpl(OReportModel& _rModel) : m_rModel(_rModel)
124         ,m_aFormatNormalizer( _rModel )
125         ,m_aConditionUpdater()
126         ,m_nLocks(0)
127 		,m_bReadOnly(sal_False)
128         ,m_bIsUndo(sal_False)
129 {
130 }
131 
132 //------------------------------------------------------------------------------
133 DBG_NAME( rpt_OXUndoEnvironment );
134 //------------------------------------------------------------------------------
OXUndoEnvironment(OReportModel & _rModel)135 OXUndoEnvironment::OXUndoEnvironment(OReportModel& _rModel)
136 				   :m_pImpl(new OXUndoEnvironmentImpl(_rModel) )
137 {
138 	DBG_CTOR( rpt_OXUndoEnvironment,NULL);
139     StartListening(m_pImpl->m_rModel);
140 }
141 
142 //------------------------------------------------------------------------------
~OXUndoEnvironment()143 OXUndoEnvironment::~OXUndoEnvironment()
144 {
145 	DBG_DTOR( rpt_OXUndoEnvironment,NULL);
146 }
147 // -----------------------------------------------------------------------------
Lock()148 void OXUndoEnvironment::Lock()
149 {
150     OSL_ENSURE(m_refCount,"Illegal call to dead object!");
151     osl_incrementInterlockedCount( &m_pImpl->m_nLocks );
152 }
UnLock()153 void OXUndoEnvironment::UnLock()
154 {
155     OSL_ENSURE(m_refCount,"Illegal call to dead object!");
156 
157     osl_decrementInterlockedCount( &m_pImpl->m_nLocks );
158 }
IsLocked() const159 sal_Bool OXUndoEnvironment::IsLocked() const { return m_pImpl->m_nLocks != 0; }
160 // -----------------------------------------------------------------------------
RemoveSection(OReportPage * _pPage)161 void OXUndoEnvironment::RemoveSection(OReportPage* _pPage)
162 {
163     if ( _pPage )
164     {
165         Reference< XInterface > xSection(_pPage->getSection());
166         if ( xSection.is() )
167 		    RemoveElement( xSection );
168     }
169 }
170 //------------------------------------------------------------------------------
Clear(const Accessor &)171 void OXUndoEnvironment::Clear(const Accessor& /*_r*/)
172 {
173 	OUndoEnvLock aLock(*this);
174 
175 #if OSL_DEBUG_LEVEL > 0
176     // TODO: LLA->OJ please describe what you are doing in this code fragment.
177     PropertySetInfoCache::iterator aIter = m_pImpl->m_aPropertySetCache.begin();
178     PropertySetInfoCache::iterator aEnd = m_pImpl->m_aPropertySetCache.end();
179     int ndbg_len = m_pImpl->m_aPropertySetCache.size();
180     ndbg_len = ndbg_len;
181     for (int idbg_ = 0; aIter != aEnd; ++aIter,++idbg_)
182     {
183         uno::Reference<beans::XPropertySet> xProp(aIter->first,uno::UNO_QUERY);
184         xProp->getPropertySetInfo();
185         int nlen = aIter->second.aProperties.size();
186         nlen = nlen;
187     }
188 #endif
189     m_pImpl->m_aPropertySetCache.clear();
190 
191 	sal_uInt16 nCount = m_pImpl->m_rModel.GetPageCount();
192 	sal_uInt16 i;
193 	for (i = 0; i < nCount; i++)
194 	{
195         OReportPage* pPage = PTR_CAST( OReportPage, m_pImpl->m_rModel.GetPage(i) );
196         RemoveSection(pPage);
197 	}
198 
199 	nCount = m_pImpl->m_rModel.GetMasterPageCount();
200 	for (i = 0; i < nCount; i++)
201 	{
202         OReportPage* pPage = PTR_CAST( OReportPage, m_pImpl->m_rModel.GetMasterPage(i) );
203         RemoveSection(pPage);
204 	}
205 
206     m_pImpl->m_aSections.clear();
207 
208 	if (IsListening(m_pImpl->m_rModel))
209 		EndListening(m_pImpl->m_rModel);
210 }
211 
212 //------------------------------------------------------------------------------
ModeChanged()213 void OXUndoEnvironment::ModeChanged()
214 {
215 	m_pImpl->m_bReadOnly = !m_pImpl->m_bReadOnly;
216 
217 	if (!m_pImpl->m_bReadOnly)
218 		StartListening(m_pImpl->m_rModel);
219 	else
220 		EndListening(m_pImpl->m_rModel);
221 }
222 
223 //------------------------------------------------------------------------------
Notify(SfxBroadcaster &,const SfxHint & rHint)224 void OXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
225 {
226     if (rHint.ISA(SfxSimpleHint) && ((SfxSimpleHint&)rHint).GetId() == SFX_HINT_MODECHANGED )
227         ModeChanged();
228 }
229 // -----------------------------------------------------------------------------
230 //	XEventListener
231 //------------------------------------------------------------------------------
disposing(const EventObject & e)232 void SAL_CALL OXUndoEnvironment::disposing(const EventObject& e) throw( RuntimeException )
233 {
234 	// check if it's an object we have cached informations about
235     Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
236     if ( xSourceSet.is() )
237     {
238         uno::Reference< report::XSection> xSection(xSourceSet,uno::UNO_QUERY);
239         if ( xSection.is() )
240             RemoveSection(xSection);
241         else
242             RemoveElement(xSourceSet);
243         /*if (!m_pImpl->m_aPropertySetCache.empty())
244 			m_pImpl->m_aPropertySetCache.erase(xSourceSet);*/
245 	}
246 }
247 
248 // XPropertyChangeListener
249 //------------------------------------------------------------------------------
propertyChange(const PropertyChangeEvent & _rEvent)250 void SAL_CALL OXUndoEnvironment::propertyChange( const PropertyChangeEvent& _rEvent ) throw(uno::RuntimeException)
251 {
252     ::osl::ClearableMutexGuard aGuard( m_pImpl->m_aMutex );
253 
254 	if ( IsLocked() )
255         return;
256 
257     Reference< XPropertySet >  xSet( _rEvent.Source, UNO_QUERY );
258 	if (!xSet.is())
259 		return;
260 
261 	dbaui::DBSubComponentController* pController = m_pImpl->m_rModel.getController();
262     if ( !pController )
263         return;
264 
265     // no Undo for transient and readonly props.
266 	// let's see if we know something about the set
267 #if OSL_DEBUG_LEVEL > 0
268     int nlen = m_pImpl->m_aPropertySetCache.size();
269     nlen = nlen;
270 #endif
271     PropertySetInfoCache::iterator objectPos = m_pImpl->m_aPropertySetCache.find(xSet);
272 	if (objectPos == m_pImpl->m_aPropertySetCache.end())
273 	{
274 		objectPos = m_pImpl->m_aPropertySetCache.insert( PropertySetInfoCache::value_type(
275             xSet, ObjectInfo()
276         ) ).first;
277 		DBG_ASSERT(objectPos != m_pImpl->m_aPropertySetCache.end(), "OXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
278 	}
279 	if ( objectPos == m_pImpl->m_aPropertySetCache.end() )
280         return;
281 
282 	// now we have access to the cached info about the set
283 	// let's see what we know about the property
284     ObjectInfo& rObjectInfo = objectPos->second;
285     PropertiesInfo::iterator aPropertyPos = rObjectInfo.aProperties.find( _rEvent.PropertyName );
286 	if ( aPropertyPos == rObjectInfo.aProperties.end() )
287 	{	// nothing 'til now ... have to change this ....
288 		// the attributes
289         Reference< XPropertySetInfo > xPSI( xSet->getPropertySetInfo(), UNO_SET_THROW );
290 		sal_Int32 nPropertyAttributes = 0;
291         try
292         {
293             if ( xPSI->hasPropertyByName( _rEvent.PropertyName ) )
294             {
295                 nPropertyAttributes = xPSI->getPropertyByName( _rEvent.PropertyName ).Attributes;
296             }
297             else
298             {
299                 // it's perfectly valid for a component to notify a change in a property which it doesn't have - as long
300                 // as it has an attribute with this name
301                 if ( !rObjectInfo.xPropertyIntrospection.is() )
302                 {
303                     if ( !m_pImpl->m_xIntrospection.is() )
304                     {
305                         ::comphelper::ComponentContext aContext( m_pImpl->m_rModel.getController()->getORB() );
306                         OSL_VERIFY( aContext.createComponent( "com.sun.star.beans.Introspection", m_pImpl->m_xIntrospection ) );
307                     }
308                     if ( m_pImpl->m_xIntrospection.is() )
309                     {
310                         Reference< XIntrospectionAccess > xIntrospection(
311                             m_pImpl->m_xIntrospection->inspect( makeAny( _rEvent.Source ) ),
312                             UNO_SET_THROW
313                         );
314                         rObjectInfo.xPropertyIntrospection.set( xIntrospection->queryAdapter( XPropertySet::static_type() ), UNO_QUERY_THROW );
315                     }
316                 }
317                 if ( rObjectInfo.xPropertyIntrospection.is() )
318                 {
319                     xPSI.set( rObjectInfo.xPropertyIntrospection->getPropertySetInfo(), UNO_SET_THROW );
320                     nPropertyAttributes = xPSI->getPropertyByName( _rEvent.PropertyName ).Attributes;
321                 }
322             }
323         }
324         catch( const Exception& )
325         {
326         	DBG_UNHANDLED_EXCEPTION();
327         }
328 		const bool bTransReadOnly =
329                     ( ( nPropertyAttributes & PropertyAttribute::READONLY ) != 0 )
330                 ||  ( ( nPropertyAttributes & PropertyAttribute::TRANSIENT ) != 0 );
331 
332 		// insert the new entry
333 		aPropertyPos = rObjectInfo.aProperties.insert( PropertiesInfo::value_type(
334             _rEvent.PropertyName,
335             PropertyInfo( bTransReadOnly )
336         ) ).first;
337 		DBG_ASSERT(aPropertyPos != rObjectInfo.aProperties.end(), "OXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
338 	}
339 
340 	implSetModified();
341 
342 	// now we have access to the cached info about the property affected
343 	// and are able to decide whether or not we need an undo action
344 
345 	// no UNDO for transient/readonly properties
346 	if ( aPropertyPos->second.bIsReadonlyOrTransient )
347         return;
348 
349     // give components with sub responsibilities a chance
350     m_pImpl->m_aFormatNormalizer.notifyPropertyChange( _rEvent );
351     m_pImpl->m_aConditionUpdater.notifyPropertyChange( _rEvent );
352 
353     aGuard.clear();
354 	// TODO: this is a potential race condition: two threads here could in theory
355 	// add their undo actions out-of-order
356 
357     ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
358     ORptUndoPropertyAction* pUndo = NULL;
359     try
360     {
361         uno::Reference< report::XSection> xSection( xSet, uno::UNO_QUERY );
362         if ( xSection.is() )
363         {
364             uno::Reference< report::XGroup> xGroup = xSection->getGroup();
365             if ( xGroup.is() )
366                 pUndo = new OUndoPropertyGroupSectionAction( m_pImpl->m_rModel, _rEvent, OGroupHelper::getMemberFunction( xSection ), xGroup );
367             else
368                 pUndo = new OUndoPropertyReportSectionAction( m_pImpl->m_rModel, _rEvent, OReportHelper::getMemberFunction( xSection ), xSection->getReportDefinition() );
369         }
370     }
371     catch(const Exception&)
372     {
373         DBG_UNHANDLED_EXCEPTION();
374     }
375 
376     if ( pUndo == NULL )
377         pUndo = new ORptUndoPropertyAction( m_pImpl->m_rModel, _rEvent );
378 
379     m_pImpl->m_rModel.GetSdrUndoManager()->AddUndoAction( pUndo );
380     pController->InvalidateAll();
381 }
382 // -----------------------------------------------------------------------------
getSection(const Reference<container::XChild> & _xContainer) const383 ::std::vector< uno::Reference< container::XChild> >::const_iterator OXUndoEnvironment::getSection(const Reference<container::XChild>& _xContainer) const
384 {
385     ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = m_pImpl->m_aSections.end();
386     if ( _xContainer.is() )
387     {
388         aFind = ::std::find(m_pImpl->m_aSections.begin(),m_pImpl->m_aSections.end(),_xContainer);
389 
390 	    if ( aFind == m_pImpl->m_aSections.end() )
391 	    {
392             Reference<container::XChild> xParent(_xContainer->getParent(),uno::UNO_QUERY);
393             aFind = getSection(xParent);
394         }
395     }
396     return aFind;
397 }
398 // XContainerListener
399 //------------------------------------------------------------------------------
elementInserted(const ContainerEvent & evt)400 void SAL_CALL OXUndoEnvironment::elementInserted(const ContainerEvent& evt) throw(uno::RuntimeException)
401 {
402 	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
403     ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );
404 
405 	// neues Object zum lauschen
406 	Reference< uno::XInterface >  xIface( evt.Element, UNO_QUERY );
407 	if ( !IsLocked() )
408     {
409         Reference< report::XReportComponent >  xReportComponent( xIface, UNO_QUERY );
410         if ( xReportComponent.is() )
411 	    {
412             Reference< report::XSection > xContainer(evt.Source,uno::UNO_QUERY);
413 
414 		    ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = getSection(xContainer.get());
415 
416 		    if ( aFind != m_pImpl->m_aSections.end() )
417 		    {
418                 OUndoEnvLock aLock(*this);
419                 try
420                 {
421                     OReportPage* pPage = m_pImpl->m_rModel.getPage(uno::Reference< report::XSection>(*aFind,uno::UNO_QUERY));
422                     OSL_ENSURE(pPage,"No page could be found for section!");
423                     if ( pPage )
424 			            pPage->insertObject(xReportComponent);
425                 }
426                 catch(uno::Exception&)
427                 {
428                     DBG_UNHANDLED_EXCEPTION();
429                 }
430 
431 		    }
432 	    }
433         else
434         {
435             uno::Reference< report::XFunctions> xContainer(evt.Source,uno::UNO_QUERY);
436             if ( xContainer.is() )
437             {
438                 m_pImpl->m_rModel.GetSdrUndoManager()->AddUndoAction(
439                     new OUndoContainerAction( m_pImpl->m_rModel, rptui::Inserted, xContainer.get(),
440                         xIface, RID_STR_UNDO_ADDFUNCTION ) );
441             }
442         }
443     }
444 
445 	AddElement(xIface);
446 
447 	implSetModified();
448 }
449 
450 //------------------------------------------------------------------------------
implSetModified()451 void OXUndoEnvironment::implSetModified()
452 {
453 	//if ( !IsLocked() )
454 	m_pImpl->m_rModel.SetModified( sal_True );
455 }
456 
457 //------------------------------------------------------------------------------
elementReplaced(const ContainerEvent & evt)458 void SAL_CALL OXUndoEnvironment::elementReplaced(const ContainerEvent& evt) throw(uno::RuntimeException)
459 {
460 	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
461     ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );
462 
463     Reference< XInterface >  xIface(evt.ReplacedElement,uno::UNO_QUERY);
464 	OSL_ENSURE(xIface.is(), "OXUndoEnvironment::elementReplaced: invalid container notification!");
465 	RemoveElement(xIface);
466 
467     xIface.set(evt.Element,uno::UNO_QUERY);
468 	AddElement(xIface);
469 
470     implSetModified();
471 }
472 
473 //------------------------------------------------------------------------------
elementRemoved(const ContainerEvent & evt)474 void SAL_CALL OXUndoEnvironment::elementRemoved(const ContainerEvent& evt) throw(uno::RuntimeException)
475 {
476 	::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
477     ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );
478 
479     Reference< uno::XInterface >  xIface( evt.Element, UNO_QUERY );
480     if ( !IsLocked() )
481     {
482 	    Reference< report::XSection > xContainer(evt.Source,uno::UNO_QUERY);
483 	    ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = getSection(xContainer.get());
484 
485         Reference< report::XReportComponent >  xReportComponent( xIface, UNO_QUERY );
486 	    if ( aFind != m_pImpl->m_aSections.end() && xReportComponent.is() )
487 	    {
488             OXUndoEnvironment::OUndoEnvLock aLock(*this);
489             try
490             {
491                 OReportPage* pPage = m_pImpl->m_rModel.getPage(uno::Reference< report::XSection >( *aFind, uno::UNO_QUERY_THROW ) );
492                 OSL_ENSURE( pPage, "OXUndoEnvironment::elementRemoved: no page for the section!" );
493                 if ( pPage )
494 			        pPage->removeSdrObject(xReportComponent);
495             }
496             catch(const uno::Exception&)
497             {
498                 DBG_UNHANDLED_EXCEPTION();
499             }
500 	    }
501         else
502         {
503             uno::Reference< report::XFunctions> xFunctions(evt.Source,uno::UNO_QUERY);
504             if ( xFunctions.is() )
505             {
506                 m_pImpl->m_rModel.GetSdrUndoManager()->AddUndoAction( new OUndoContainerAction(
507                     m_pImpl->m_rModel, rptui::Removed, xFunctions.get(), xIface, RID_STR_UNDO_ADDFUNCTION ) );
508             }
509         }
510     }
511 
512 	if ( xIface.is() )
513 		RemoveElement(xIface);
514 
515 	implSetModified();
516 }
517 
518 //------------------------------------------------------------------------------
modified(const EventObject &)519 void SAL_CALL OXUndoEnvironment::modified( const EventObject& /*aEvent*/ ) throw (RuntimeException)
520 {
521     implSetModified();
522 }
523 
524 //------------------------------------------------------------------------------
AddSection(const Reference<report::XSection> & _xSection)525 void OXUndoEnvironment::AddSection(const Reference< report::XSection > & _xSection)
526 {
527 	OUndoEnvLock aLock(*this);
528     try
529     {
530 	    uno::Reference<container::XChild> xChild = _xSection.get();
531 	    uno::Reference<report::XGroup> xGroup(xChild->getParent(),uno::UNO_QUERY);
532 	    m_pImpl->m_aSections.push_back(xChild);
533 	    Reference< XInterface >  xInt(_xSection);
534 	    AddElement(xInt);
535     }
536     catch(const uno::Exception&)
537     {
538         DBG_UNHANDLED_EXCEPTION();
539     }
540 }
541 
542 //------------------------------------------------------------------------------
RemoveSection(const Reference<report::XSection> & _xSection)543 void OXUndoEnvironment::RemoveSection(const Reference< report::XSection > & _xSection)
544 {
545 	OUndoEnvLock aLock(*this);
546     try
547     {
548 	    uno::Reference<container::XChild> xChild(_xSection.get());
549 	    m_pImpl->m_aSections.erase(::std::remove(m_pImpl->m_aSections.begin(),m_pImpl->m_aSections.end(),
550 		    xChild), m_pImpl->m_aSections.end());
551 	    Reference< XInterface >  xInt(_xSection);
552 	    RemoveElement(xInt);
553     }
554     catch(uno::Exception&){}
555 }
556 
557 //------------------------------------------------------------------------------
TogglePropertyListening(const Reference<XInterface> & Element)558 void OXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
559 {
560 	// am Container horchen
561 	Reference< XIndexAccess >  xContainer(Element, UNO_QUERY);
562 	if (xContainer.is())
563 	{
564 		Reference< XInterface > xInterface;
565 		sal_Int32 nCount = xContainer->getCount();
566 		for(sal_Int32 i = 0;i != nCount;++i)
567 		{
568 			xInterface.set(xContainer->getByIndex( i ),uno::UNO_QUERY);
569 			TogglePropertyListening(xInterface);
570 		}
571 	}
572 
573 	Reference< XPropertySet >  xSet(Element, UNO_QUERY);
574 	if (xSet.is())
575 	{
576 		if (!m_pImpl->m_bReadOnly)
577 			xSet->addPropertyChangeListener( ::rtl::OUString(), this );
578 		else
579 			xSet->removePropertyChangeListener( ::rtl::OUString(), this );
580 	}
581 }
582 
583 
584 //------------------------------------------------------------------------------
switchListening(const Reference<XIndexAccess> & _rxContainer,bool _bStartListening)585 void OXUndoEnvironment::switchListening( const Reference< XIndexAccess >& _rxContainer, bool _bStartListening ) SAL_THROW(())
586 {
587     OSL_PRECOND( _rxContainer.is(), "OXUndoEnvironment::switchListening: invalid container!" );
588     if ( !_rxContainer.is() )
589         return;
590 
591     try
592     {
593         // also handle all children of this element
594 		Reference< XInterface > xInterface;
595         sal_Int32 nCount = _rxContainer->getCount();
596 		for(sal_Int32 i = 0;i != nCount;++i)
597 		{
598 			xInterface.set(_rxContainer->getByIndex( i ),uno::UNO_QUERY);
599             if ( _bStartListening )
600 		        AddElement( xInterface );
601             else
602 		        RemoveElement( xInterface );
603 	    }
604 
605         // be notified of any changes in the container elements
606 	    Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
607         // OSL_ENSURE( xSimpleContainer.is(), "OXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
608 	    if ( xSimpleContainer.is() )
609         {
610             if ( _bStartListening )
611 		        xSimpleContainer->addContainerListener( this );
612             else
613 		        xSimpleContainer->removeContainerListener( this );
614         }
615     }
616     catch( const Exception& )
617     {
618     	DBG_UNHANDLED_EXCEPTION();
619     }
620 }
621 
622 //------------------------------------------------------------------------------
switchListening(const Reference<XInterface> & _rxObject,bool _bStartListening)623 void OXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening ) SAL_THROW(())
624 {
625     OSL_PRECOND( _rxObject.is(), "OXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
626 
627     try
628     {
629         if ( !m_pImpl->m_bReadOnly )
630         {
631             Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
632 	        if ( xProps.is() )
633             {
634                 if ( _bStartListening )
635     		        xProps->addPropertyChangeListener( ::rtl::OUString(), this );
636                 else
637     	    	    xProps->removePropertyChangeListener( ::rtl::OUString(), this );
638             }
639         }
640 
641         Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
642         if ( xBroadcaster.is() )
643         {
644             if ( _bStartListening )
645                 xBroadcaster->addModifyListener( this );
646             else
647                 xBroadcaster->removeModifyListener( this );
648         }
649     }
650     catch( const Exception& )
651     {
652     	//OSL_ENSURE( sal_False, "OXUndoEnvironment::switchListening: caught an exception!" );
653     }
654 }
655 
656 //------------------------------------------------------------------------------
AddElement(const Reference<XInterface> & _rxElement)657 void OXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
658 {
659 	if ( !IsLocked() )
660 		m_pImpl->m_aFormatNormalizer.notifyElementInserted( _rxElement );
661 
662     // if it's a container, start listening at all elements
663     Reference< XIndexAccess > xContainer( _rxElement, UNO_QUERY );
664 	if ( xContainer.is() )
665         switchListening( xContainer, true );
666 
667     switchListening( _rxElement, true );
668 }
669 
670 //------------------------------------------------------------------------------
RemoveElement(const Reference<XInterface> & _rxElement)671 void OXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
672 {
673     uno::Reference<beans::XPropertySet> xProp(_rxElement,uno::UNO_QUERY);
674     if (!m_pImpl->m_aPropertySetCache.empty())
675         m_pImpl->m_aPropertySetCache.erase(xProp);
676     switchListening( _rxElement, false );
677 
678 	Reference< XIndexAccess > xContainer( _rxElement, UNO_QUERY );
679     if ( xContainer.is() )
680         switchListening( xContainer, false );
681 }
682 
SetUndoMode(sal_Bool _bUndo)683 void OXUndoEnvironment::SetUndoMode(sal_Bool _bUndo)
684 {
685     m_pImpl->m_bIsUndo = _bUndo;
686 }
687 
IsUndoMode() const688 sal_Bool OXUndoEnvironment::IsUndoMode() const
689 {
690     return m_pImpl->m_bIsUndo;
691 }
692 //============================================================================
693 } // rptui
694 //============================================================================
695