Update ooo320-m1
[ooovba.git] / reportdesign / source / core / sdr / UndoEnv.cxx
blob3a0dc2569f3153c8cb4c3c4a905e0c826b2f30af
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: UndoActions.cxx,v $
10 * $Revision: 1.8 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "UndoActions.hxx"
32 #include "UndoEnv.hxx"
33 #include "formatnormalizer.hxx"
34 #include "conditionupdater.hxx"
35 #include "corestrings.hrc"
36 #include "rptui_slotid.hrc"
37 #include "RptDef.hxx"
38 #include "ModuleHelper.hxx"
39 #include "RptObject.hxx"
40 #include "RptPage.hxx"
41 #include "RptResId.hrc"
42 #include "RptModel.hxx"
44 /** === begin UNO includes === **/
45 #include <com/sun/star/script/XEventAttacherManager.hpp>
46 #include <com/sun/star/container/XChild.hpp>
47 #include <com/sun/star/container/XNameContainer.hpp>
48 #include <com/sun/star/beans/PropertyAttribute.hpp>
49 #include <com/sun/star/util/XModifyBroadcaster.hpp>
50 /** === end UNO includes === **/
52 #include <connectivity/dbtools.hxx>
53 #include <svtools/smplhint.hxx>
54 #include <tools/diagnose_ex.h>
55 #include <comphelper/stl_types.hxx>
56 #include <vcl/svapp.hxx>
57 #include <dbaccess/singledoccontroller.hxx>
58 #include <svx/unoshape.hxx>
59 #include <vos/mutex.hxx>
61 namespace rptui
63 using namespace ::com::sun::star;
64 using namespace uno;
65 using namespace lang;
66 using namespace script;
67 using namespace beans;
68 using namespace awt;
69 using namespace util;
70 using namespace container;
71 using namespace report;
72 //----------------------------------------------------------------------------
75 DECLARE_STL_USTRINGACCESS_MAP(bool, AllProperties);
76 DECLARE_STL_STDKEY_MAP(uno::Reference< beans::XPropertySet >, AllProperties, PropertySetInfoCache);
78 // -----------------------------------------------------------------------------
80 class OXUndoEnvironmentImpl
82 OXUndoEnvironmentImpl(OXUndoEnvironmentImpl&);
83 void operator =(OXUndoEnvironmentImpl&);
84 public:
85 OReportModel& m_rModel;
86 PropertySetInfoCache m_aPropertySetCache;
87 FormatNormalizer m_aFormatNormalizer;
88 ConditionUpdater m_aConditionUpdater;
89 ::osl::Mutex m_aMutex;
90 ::std::vector< uno::Reference< container::XChild> > m_aSections;
91 oslInterlockedCount m_nLocks;
92 sal_Bool m_bReadOnly;
93 sal_Bool m_bIsUndo;
95 OXUndoEnvironmentImpl(OReportModel& _rModel);
98 OXUndoEnvironmentImpl::OXUndoEnvironmentImpl(OReportModel& _rModel) : m_rModel(_rModel)
99 ,m_aFormatNormalizer( _rModel )
100 ,m_aConditionUpdater()
101 ,m_nLocks(0)
102 ,m_bReadOnly(sal_False)
103 ,m_bIsUndo(sal_False)
107 //------------------------------------------------------------------------------
108 DBG_NAME( rpt_OXUndoEnvironment );
109 //------------------------------------------------------------------------------
110 OXUndoEnvironment::OXUndoEnvironment(OReportModel& _rModel)
111 :m_pImpl(new OXUndoEnvironmentImpl(_rModel) )
113 DBG_CTOR( rpt_OXUndoEnvironment,NULL);
114 StartListening(m_pImpl->m_rModel);
117 //------------------------------------------------------------------------------
118 OXUndoEnvironment::~OXUndoEnvironment()
120 DBG_DTOR( rpt_OXUndoEnvironment,NULL);
122 // -----------------------------------------------------------------------------
123 void OXUndoEnvironment::Lock()
125 OSL_ENSURE(m_refCount,"Illegal call to dead object!");
126 osl_incrementInterlockedCount( &m_pImpl->m_nLocks );
128 void OXUndoEnvironment::UnLock()
130 OSL_ENSURE(m_refCount,"Illegal call to dead object!");
132 osl_decrementInterlockedCount( &m_pImpl->m_nLocks );
134 sal_Bool OXUndoEnvironment::IsLocked() const { return m_pImpl->m_nLocks != 0; }
135 // -----------------------------------------------------------------------------
136 void OXUndoEnvironment::RemoveSection(OReportPage* _pPage)
138 if ( _pPage )
140 Reference< XInterface > xSection(_pPage->getSection());
141 if ( xSection.is() )
142 RemoveElement( xSection );
145 //------------------------------------------------------------------------------
146 void OXUndoEnvironment::Clear(const Accessor& /*_r*/)
148 OUndoEnvLock aLock(*this);
150 #if OSL_DEBUG_LEVEL > 0
151 // TODO: LLA->OJ please describe what you are doing in this code fragment.
152 PropertySetInfoCache::iterator aIter = m_pImpl->m_aPropertySetCache.begin();
153 PropertySetInfoCache::iterator aEnd = m_pImpl->m_aPropertySetCache.end();
154 int ndbg_len = m_pImpl->m_aPropertySetCache.size();
155 ndbg_len = ndbg_len;
156 for (int idbg_ = 0; aIter != aEnd; ++aIter,++idbg_)
158 uno::Reference<beans::XPropertySet> xProp(aIter->first,uno::UNO_QUERY);
159 xProp->getPropertySetInfo();
160 int nlen = aIter->second.size();
161 nlen = nlen;
163 #endif
164 m_pImpl->m_aPropertySetCache.clear();
166 sal_uInt16 nCount = m_pImpl->m_rModel.GetPageCount();
167 sal_uInt16 i;
168 for (i = 0; i < nCount; i++)
170 OReportPage* pPage = PTR_CAST( OReportPage, m_pImpl->m_rModel.GetPage(i) );
171 RemoveSection(pPage);
174 nCount = m_pImpl->m_rModel.GetMasterPageCount();
175 for (i = 0; i < nCount; i++)
177 OReportPage* pPage = PTR_CAST( OReportPage, m_pImpl->m_rModel.GetMasterPage(i) );
178 RemoveSection(pPage);
181 m_pImpl->m_aSections.clear();
183 if (IsListening(m_pImpl->m_rModel))
184 EndListening(m_pImpl->m_rModel);
187 //------------------------------------------------------------------------------
188 void OXUndoEnvironment::ModeChanged()
190 m_pImpl->m_bReadOnly = !m_pImpl->m_bReadOnly;
192 if (!m_pImpl->m_bReadOnly)
193 StartListening(m_pImpl->m_rModel);
194 else
195 EndListening(m_pImpl->m_rModel);
198 //------------------------------------------------------------------------------
199 void OXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
201 if (rHint.ISA(SfxSimpleHint) && ((SfxSimpleHint&)rHint).GetId() == SFX_HINT_MODECHANGED )
202 ModeChanged();
204 // -----------------------------------------------------------------------------
205 // XEventListener
206 //------------------------------------------------------------------------------
207 void SAL_CALL OXUndoEnvironment::disposing(const EventObject& e) throw( RuntimeException )
209 // check if it's an object we have cached informations about
210 Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
211 if ( xSourceSet.is() )
213 uno::Reference< report::XSection> xSection(xSourceSet,uno::UNO_QUERY);
214 if ( xSection.is() )
215 RemoveSection(xSection);
216 else
217 RemoveElement(xSourceSet);
218 /*if (!m_pImpl->m_aPropertySetCache.empty())
219 m_pImpl->m_aPropertySetCache.erase(xSourceSet);*/
223 // XPropertyChangeListener
224 //------------------------------------------------------------------------------
225 void SAL_CALL OXUndoEnvironment::propertyChange( const PropertyChangeEvent& _rEvent ) throw(uno::RuntimeException)
227 ::osl::ClearableMutexGuard aGuard( m_pImpl->m_aMutex );
229 if ( IsLocked() )
230 return;
232 Reference< XPropertySet > xSet( _rEvent.Source, UNO_QUERY );
233 if (!xSet.is())
234 return;
236 dbaui::OSingleDocumentController* pController = m_pImpl->m_rModel.getController();
237 if ( !pController )
238 return;
240 // no Undo for transient and readonly props.
241 // let's see if we know something about the set
242 #if OSL_DEBUG_LEVEL > 0
243 int nlen = m_pImpl->m_aPropertySetCache.size();
244 nlen = nlen;
245 #endif
246 PropertySetInfoCache::iterator aSetPos = m_pImpl->m_aPropertySetCache.find(xSet);
247 if (aSetPos == m_pImpl->m_aPropertySetCache.end())
249 AllProperties aNewEntry;
250 aSetPos = m_pImpl->m_aPropertySetCache.insert(PropertySetInfoCache::value_type(xSet,aNewEntry)).first;
251 DBG_ASSERT(aSetPos != m_pImpl->m_aPropertySetCache.end(), "OXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
253 if ( aSetPos == m_pImpl->m_aPropertySetCache.end() )
254 return;
256 // now we have access to the cached info about the set
257 // let's see what we know about the property
258 AllProperties& rPropInfos = aSetPos->second;
259 AllPropertiesIterator aPropertyPos = rPropInfos.find( _rEvent.PropertyName );
260 if (aPropertyPos == rPropInfos.end())
261 { // nothing 'til now ... have to change this ....
262 // the attributes
263 INT32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName( _rEvent.PropertyName ).Attributes;
264 bool bTransReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
266 // insert the new entry
267 aPropertyPos = rPropInfos.insert( AllProperties::value_type( _rEvent.PropertyName, bTransReadOnly ) ).first;
268 DBG_ASSERT(aPropertyPos != rPropInfos.end(), "OXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
271 implSetModified();
273 // now we have access to the cached info about the property affected
274 // and are able to decide wether or not we need an undo action
276 // no UNDO for transient/readonly properties
277 if ( aPropertyPos->second )
278 return;
280 // give components with sub responsibilities a chance
281 m_pImpl->m_aFormatNormalizer.notifyPropertyChange( _rEvent );
282 m_pImpl->m_aConditionUpdater.notifyPropertyChange( _rEvent );
284 aGuard.clear();
285 // TODO: this is a potential race condition: two threads here could in theory
286 // add their undo actions out-of-order
288 ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
289 ORptUndoPropertyAction* pUndo = NULL;
292 uno::Reference< report::XSection> xSection( xSet, uno::UNO_QUERY );
293 if ( xSection.is() )
295 uno::Reference< report::XGroup> xGroup = xSection->getGroup();
296 if ( xGroup.is() )
297 pUndo = new OUndoPropertyGroupSectionAction( m_pImpl->m_rModel, _rEvent, OGroupHelper::getMemberFunction( xSection ), xGroup );
298 else
299 pUndo = new OUndoPropertyReportSectionAction( m_pImpl->m_rModel, _rEvent, OReportHelper::getMemberFunction( xSection ), xSection->getReportDefinition() );
302 catch(const Exception&)
304 DBG_UNHANDLED_EXCEPTION();
307 if ( pUndo == NULL )
308 pUndo = new ORptUndoPropertyAction( m_pImpl->m_rModel, _rEvent );
310 pController->addUndoActionAndInvalidate(pUndo);
311 pController->InvalidateAll();
313 // -----------------------------------------------------------------------------
314 ::std::vector< uno::Reference< container::XChild> >::const_iterator OXUndoEnvironment::getSection(const Reference<container::XChild>& _xContainer) const
316 ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = m_pImpl->m_aSections.end();
317 if ( _xContainer.is() )
319 aFind = ::std::find(m_pImpl->m_aSections.begin(),m_pImpl->m_aSections.end(),_xContainer);
321 if ( aFind == m_pImpl->m_aSections.end() )
323 Reference<container::XChild> xParent(_xContainer->getParent(),uno::UNO_QUERY);
324 aFind = getSection(xParent);
327 return aFind;
329 // XContainerListener
330 //------------------------------------------------------------------------------
331 void SAL_CALL OXUndoEnvironment::elementInserted(const ContainerEvent& evt) throw(uno::RuntimeException)
333 ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
334 ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );
336 // neues Object zum lauschen
337 Reference< uno::XInterface > xIface( evt.Element, UNO_QUERY );
338 if ( !IsLocked() )
340 Reference< report::XReportComponent > xReportComponent( xIface, UNO_QUERY );
341 if ( xReportComponent.is() )
343 Reference< report::XSection > xContainer(evt.Source,uno::UNO_QUERY);
345 ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = getSection(xContainer.get());
347 if ( aFind != m_pImpl->m_aSections.end() )
349 OUndoEnvLock aLock(*this);
352 OReportPage* pPage = m_pImpl->m_rModel.getPage(uno::Reference< report::XSection>(*aFind,uno::UNO_QUERY));
353 OSL_ENSURE(pPage,"No page could be found for section!");
354 if ( pPage )
355 pPage->insertObject(xReportComponent);
357 catch(uno::Exception&)
359 DBG_UNHANDLED_EXCEPTION();
364 else
366 uno::Reference< report::XFunctions> xContainer(evt.Source,uno::UNO_QUERY);
367 if ( xContainer.is() )
369 dbaui::OSingleDocumentController* pController = m_pImpl->m_rModel.getController();
370 pController->addUndoActionAndInvalidate(new OUndoContainerAction(m_pImpl->m_rModel
371 ,rptui::Inserted
372 ,xContainer.get()
373 ,xIface
374 ,RID_STR_UNDO_ADDFUNCTION));
379 AddElement(xIface);
381 implSetModified();
384 //------------------------------------------------------------------------------
385 void OXUndoEnvironment::implSetModified()
387 //if ( !IsLocked() )
388 m_pImpl->m_rModel.SetModified( sal_True );
391 //------------------------------------------------------------------------------
392 void SAL_CALL OXUndoEnvironment::elementReplaced(const ContainerEvent& evt) throw(uno::RuntimeException)
394 ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
395 ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );
397 Reference< XInterface > xIface(evt.ReplacedElement,uno::UNO_QUERY);
398 OSL_ENSURE(xIface.is(), "OXUndoEnvironment::elementReplaced: invalid container notification!");
399 RemoveElement(xIface);
401 xIface.set(evt.Element,uno::UNO_QUERY);
402 AddElement(xIface);
404 implSetModified();
407 //------------------------------------------------------------------------------
408 void SAL_CALL OXUndoEnvironment::elementRemoved(const ContainerEvent& evt) throw(uno::RuntimeException)
410 ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );
411 ::osl::MutexGuard aGuard( m_pImpl->m_aMutex );
413 Reference< uno::XInterface > xIface( evt.Element, UNO_QUERY );
414 if ( !IsLocked() )
416 Reference< report::XSection > xContainer(evt.Source,uno::UNO_QUERY);
417 ::std::vector< uno::Reference< container::XChild> >::const_iterator aFind = getSection(xContainer.get());
419 Reference< report::XReportComponent > xReportComponent( xIface, UNO_QUERY );
420 if ( aFind != m_pImpl->m_aSections.end() && xReportComponent.is() )
422 OXUndoEnvironment::OUndoEnvLock aLock(*this);
425 OReportPage* pPage = m_pImpl->m_rModel.getPage(uno::Reference< report::XSection >( *aFind, uno::UNO_QUERY_THROW ) );
426 OSL_ENSURE( pPage, "OXUndoEnvironment::elementRemoved: no page for the section!" );
427 if ( pPage )
428 pPage->removeSdrObject(xReportComponent);
430 catch(const uno::Exception&)
432 DBG_UNHANDLED_EXCEPTION();
435 else
437 uno::Reference< report::XFunctions> xFunctions(evt.Source,uno::UNO_QUERY);
438 if ( xFunctions.is() )
440 dbaui::OSingleDocumentController* pController = m_pImpl->m_rModel.getController();
441 pController->addUndoActionAndInvalidate(new OUndoContainerAction(m_pImpl->m_rModel
442 ,rptui::Removed
443 ,xFunctions.get()
444 ,xIface
445 ,RID_STR_UNDO_ADDFUNCTION));
450 if ( xIface.is() )
451 RemoveElement(xIface);
453 implSetModified();
456 //------------------------------------------------------------------------------
457 void SAL_CALL OXUndoEnvironment::modified( const EventObject& /*aEvent*/ ) throw (RuntimeException)
459 implSetModified();
462 //------------------------------------------------------------------------------
463 void OXUndoEnvironment::AddSection(const Reference< report::XSection > & _xSection)
465 OUndoEnvLock aLock(*this);
468 uno::Reference<container::XChild> xChild = _xSection.get();
469 uno::Reference<report::XGroup> xGroup(xChild->getParent(),uno::UNO_QUERY);
470 m_pImpl->m_aSections.push_back(xChild);
471 Reference< XInterface > xInt(_xSection);
472 AddElement(xInt);
474 catch(const uno::Exception&)
476 DBG_UNHANDLED_EXCEPTION();
480 //------------------------------------------------------------------------------
481 void OXUndoEnvironment::RemoveSection(const Reference< report::XSection > & _xSection)
483 OUndoEnvLock aLock(*this);
486 uno::Reference<container::XChild> xChild(_xSection.get());
487 m_pImpl->m_aSections.erase(::std::remove(m_pImpl->m_aSections.begin(),m_pImpl->m_aSections.end(),
488 xChild), m_pImpl->m_aSections.end());
489 Reference< XInterface > xInt(_xSection);
490 RemoveElement(xInt);
492 catch(uno::Exception&){}
495 //------------------------------------------------------------------------------
496 void OXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
498 // am Container horchen
499 Reference< XIndexAccess > xContainer(Element, UNO_QUERY);
500 if (xContainer.is())
502 Reference< XInterface > xInterface;
503 sal_Int32 nCount = xContainer->getCount();
504 for(sal_Int32 i = 0;i != nCount;++i)
506 xInterface.set(xContainer->getByIndex( i ),uno::UNO_QUERY);
507 TogglePropertyListening(xInterface);
511 Reference< XPropertySet > xSet(Element, UNO_QUERY);
512 if (xSet.is())
514 if (!m_pImpl->m_bReadOnly)
515 xSet->addPropertyChangeListener( ::rtl::OUString(), this );
516 else
517 xSet->removePropertyChangeListener( ::rtl::OUString(), this );
522 //------------------------------------------------------------------------------
523 void OXUndoEnvironment::switchListening( const Reference< XIndexAccess >& _rxContainer, bool _bStartListening ) SAL_THROW(())
525 OSL_PRECOND( _rxContainer.is(), "OXUndoEnvironment::switchListening: invalid container!" );
526 if ( !_rxContainer.is() )
527 return;
531 // also handle all children of this element
532 Reference< XInterface > xInterface;
533 sal_Int32 nCount = _rxContainer->getCount();
534 for(sal_Int32 i = 0;i != nCount;++i)
536 xInterface.set(_rxContainer->getByIndex( i ),uno::UNO_QUERY);
537 if ( _bStartListening )
538 AddElement( xInterface );
539 else
540 RemoveElement( xInterface );
543 // be notified of any changes in the container elements
544 Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
545 // OSL_ENSURE( xSimpleContainer.is(), "OXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
546 if ( xSimpleContainer.is() )
548 if ( _bStartListening )
549 xSimpleContainer->addContainerListener( this );
550 else
551 xSimpleContainer->removeContainerListener( this );
554 catch( const Exception& )
556 DBG_UNHANDLED_EXCEPTION();
560 //------------------------------------------------------------------------------
561 void OXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening ) SAL_THROW(())
563 OSL_PRECOND( _rxObject.is(), "OXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
567 if ( !m_pImpl->m_bReadOnly )
569 Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
570 if ( xProps.is() )
572 if ( _bStartListening )
573 xProps->addPropertyChangeListener( ::rtl::OUString(), this );
574 else
575 xProps->removePropertyChangeListener( ::rtl::OUString(), this );
579 Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
580 if ( xBroadcaster.is() )
582 if ( _bStartListening )
583 xBroadcaster->addModifyListener( this );
584 else
585 xBroadcaster->removeModifyListener( this );
588 catch( const Exception& )
590 //OSL_ENSURE( sal_False, "OXUndoEnvironment::switchListening: caught an exception!" );
594 //------------------------------------------------------------------------------
595 void OXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
597 if ( !IsLocked() )
598 m_pImpl->m_aFormatNormalizer.notifyElementInserted( _rxElement );
600 // if it's a container, start listening at all elements
601 Reference< XIndexAccess > xContainer( _rxElement, UNO_QUERY );
602 if ( xContainer.is() )
603 switchListening( xContainer, true );
605 switchListening( _rxElement, true );
608 //------------------------------------------------------------------------------
609 void OXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
611 uno::Reference<beans::XPropertySet> xProp(_rxElement,uno::UNO_QUERY);
612 if (!m_pImpl->m_aPropertySetCache.empty())
613 m_pImpl->m_aPropertySetCache.erase(xProp);
614 switchListening( _rxElement, false );
616 Reference< XIndexAccess > xContainer( _rxElement, UNO_QUERY );
617 if ( xContainer.is() )
618 switchListening( xContainer, false );
621 void OXUndoEnvironment::SetUndoMode(sal_Bool _bUndo)
623 m_pImpl->m_bIsUndo = _bUndo;
626 sal_Bool OXUndoEnvironment::IsUndoMode() const
628 return m_pImpl->m_bIsUndo;
630 //============================================================================
631 } // rptui
632 //============================================================================