1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 /**************************************************************************
32 **************************************************************************
34 *************************************************************************/
36 #include "osl/diagnose.h"
37 #include "rtl/ref.hxx"
38 #include "cppuhelper/weak.hxx"
40 #include "comphelper/namedvaluecollection.hxx"
41 #include "comphelper/documentinfo.hxx"
43 #include "com/sun/star/awt/XTopWindow.hpp"
44 #include "com/sun/star/beans/XPropertySet.hpp"
45 #include "com/sun/star/container/XEnumerationAccess.hpp"
46 #include "com/sun/star/document/XStorageBasedDocument.hpp"
47 #include "com/sun/star/frame/XStorable.hpp"
48 #include "com/sun/star/lang/DisposedException.hpp"
49 #include "com/sun/star/util/XCloseBroadcaster.hpp"
51 #include "tdoc_docmgr.hxx"
53 using namespace com::sun::star
;
54 using namespace tdoc_ucp
;
55 using ::comphelper::DocumentInfo
;
57 //=========================================================================
58 //=========================================================================
60 // OfficeDocumentsCloseListener Implementation.
62 //=========================================================================
63 //=========================================================================
65 //=========================================================================
67 // util::XCloseListener
69 //=========================================================================
72 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
73 const lang::EventObject
& /*Source*/, sal_Bool
/*GetsOwnership*/ )
74 throw ( util::CloseVetoException
,
75 uno::RuntimeException
)
79 //=========================================================================
80 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
81 const lang::EventObject
& Source
)
82 throw ( uno::RuntimeException
)
84 document::EventObject aDocEvent
;
85 aDocEvent
.Source
= Source
.Source
;
86 aDocEvent
.EventName
= rtl::OUString(
87 RTL_CONSTASCII_USTRINGPARAM( "OfficeDocumentsListener::notifyClosing" ) );
88 m_pManager
->notifyEvent( aDocEvent
);
91 //=========================================================================
93 // lang::XEventListener (base of util::XCloseListener)
95 //=========================================================================
98 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
99 const lang::EventObject
& /*Source*/ )
100 throw ( uno::RuntimeException
)
104 //=========================================================================
105 //=========================================================================
107 // OfficeDocumentsManager Implementation.
109 //=========================================================================
110 //=========================================================================
112 OfficeDocumentsManager::OfficeDocumentsManager(
113 const uno::Reference
< lang::XMultiServiceFactory
> & xSMgr
,
114 OfficeDocumentsEventListener
* pDocEventListener
)
116 m_xDocEvtNotifier( createDocumentEventNotifier( xSMgr
) ),
117 m_pDocEventListener( pDocEventListener
),
118 m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
120 if ( m_xDocEvtNotifier
.is() )
122 // Order is important (multithreaded environment)
123 m_xDocEvtNotifier
->addEventListener( this );
124 buildDocumentsList();
128 //=========================================================================
130 OfficeDocumentsManager::~OfficeDocumentsManager()
132 //OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
133 // no need to assert this: Normal shutdown of OOo could already trigger it, since the order in which
134 // objects are actually released/destroyed upon shutdown is not defined. And when we arrive *here*,
135 // OOo *is* shutting down currently, since we're held by the TDOC provider, which is disposed
139 //=========================================================================
140 void OfficeDocumentsManager::destroy()
142 if ( m_xDocEvtNotifier
.is() )
143 m_xDocEvtNotifier
->removeEventListener( this );
146 //=========================================================================
148 getDocumentId( const uno::Reference
< uno::XInterface
> & xDoc
)
152 // Try to get the UID directly from the document.
153 uno::Reference
< beans::XPropertySet
> xPropSet( xDoc
, uno::UNO_QUERY
);
158 uno::Any aValue
= xPropSet
->getPropertyValue(
160 RTL_CONSTASCII_USTRINGPARAM( "RuntimeUID" ) ) );
163 catch ( beans::UnknownPropertyException
const & )
165 // Not actually an error. Property is optional.
167 catch ( lang::WrappedTargetException
const & )
169 OSL_FAIL( "Caught WrappedTargetException!" );
175 // fallback: generate UID from document's this pointer.
176 // normalize the interface pointer first. Else, calls with different
177 // interfaces to the same object (say, XFoo and XBar) will produce
179 uno::Reference
< uno::XInterface
> xNormalizedIFace( xDoc
, uno::UNO_QUERY
);
180 sal_Int64 nId
= reinterpret_cast< sal_Int64
>( xNormalizedIFace
.get() );
181 aId
= rtl::OUString::valueOf( nId
);
184 OSL_ENSURE( !aId
.isEmpty(), "getDocumentId - Empty id!" );
188 //=========================================================================
190 // document::XEventListener
192 //=========================================================================
195 void SAL_CALL
OfficeDocumentsManager::notifyEvent(
196 const document::EventObject
& Event
)
197 throw ( uno::RuntimeException
)
200 Events documentation: OOo Developer's Guide / Writing UNO Components / Jobs
203 if ( Event
.EventName
== "OnLoadFinished" // document loaded
204 || Event
.EventName
== "OnCreate" ) // document created
206 if ( isOfficeDocument( Event
.Source
) )
208 osl::MutexGuard
aGuard( m_aMtx
);
210 uno::Reference
< frame::XModel
>
211 xModel( Event
.Source
, uno::UNO_QUERY
);
212 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
214 DocumentList::const_iterator it
= m_aDocs
.begin();
215 while ( it
!= m_aDocs
.end() )
217 if ( (*it
).second
.xModel
== xModel
)
225 if ( it
== m_aDocs
.end() )
229 uno::Reference
< document::XStorageBasedDocument
>
230 xDoc( Event
.Source
, uno::UNO_QUERY
);
231 OSL_ENSURE( xDoc
.is(), "Got no document::XStorageBasedDocument!" );
233 uno::Reference
< embed::XStorage
> xStorage
234 = xDoc
->getDocumentStorage();
235 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
237 rtl:: OUString aDocId
= getDocumentId( Event
.Source
);
238 rtl:: OUString aTitle
= DocumentInfo::getDocumentTitle(
239 uno::Reference
< frame::XModel
>( Event
.Source
, uno::UNO_QUERY
) );
241 m_aDocs
[ aDocId
] = StorageInfo( aTitle
, xStorage
, xModel
);
243 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
244 Event
.Source
, uno::UNO_QUERY
);
245 OSL_ENSURE( xCloseBroadcaster
.is(),
246 "OnLoadFinished/OnCreate event: got no close broadcaster!" );
248 if ( xCloseBroadcaster
.is() )
249 xCloseBroadcaster
->addCloseListener( m_xDocCloseListener
);
251 // Propagate document closure.
252 OSL_ENSURE( m_pDocEventListener
,
253 "OnLoadFinished/OnCreate event: no owner for insert event propagation!" );
255 if ( m_pDocEventListener
)
256 m_pDocEventListener
->notifyDocumentOpened( aDocId
);
260 else if ( Event
.EventName
== "OfficeDocumentsListener::notifyClosing" )
262 if ( isOfficeDocument( Event
.Source
) )
264 // Document has been closed (unloaded)
266 // #163732# - Official event "OnUnload" does not work here. Event
267 // gets fired to early. Other OnUnload listeners called after this
268 // listener may still need TDOC access to the document. Remove the
269 // document from TDOC docs list on XCloseListener::notifyClosing.
270 // See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.
272 osl::MutexGuard
aGuard( m_aMtx
);
274 uno::Reference
< frame::XModel
>
275 xModel( Event
.Source
, uno::UNO_QUERY
);
276 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
278 DocumentList::iterator it
= m_aDocs
.begin();
279 while ( it
!= m_aDocs
.end() )
281 if ( (*it
).second
.xModel
== xModel
)
283 // Propagate document closure.
284 OSL_ENSURE( m_pDocEventListener
,
285 "OnUnload event: no owner for close event propagation!" );
287 if ( m_pDocEventListener
)
289 rtl::OUString
aDocId( (*it
).first
);
290 m_pDocEventListener
->notifyDocumentClosed( aDocId
);
297 OSL_ENSURE( it
!= m_aDocs
.end(),
298 "OnUnload event notified for unknown document!" );
300 if ( it
!= m_aDocs
.end() )
302 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
303 Event
.Source
, uno::UNO_QUERY
);
304 OSL_ENSURE( xCloseBroadcaster
.is(),
305 "OnUnload event: got no XCloseBroadcaster from XModel" );
307 if ( xCloseBroadcaster
.is() )
308 xCloseBroadcaster
->removeCloseListener( m_xDocCloseListener
);
314 else if ( Event
.EventName
== "OnSaveDone" )
316 if ( isOfficeDocument( Event
.Source
) )
318 osl::MutexGuard
aGuard( m_aMtx
);
320 uno::Reference
< frame::XModel
>
321 xModel( Event
.Source
, uno::UNO_QUERY
);
322 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
324 DocumentList::iterator it
= m_aDocs
.begin();
325 while ( it
!= m_aDocs
.end() )
327 if ( (*it
).second
.xModel
== xModel
)
329 // Storage gets exchanged while saving.
330 uno::Reference
< document::XStorageBasedDocument
>
331 xDoc( Event
.Source
, uno::UNO_QUERY
);
332 OSL_ENSURE( xDoc
.is(),
333 "Got no document::XStorageBasedDocument!" );
335 uno::Reference
< embed::XStorage
> xStorage
336 = xDoc
->getDocumentStorage();
337 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
339 (*it
).second
.xStorage
= xStorage
;
345 OSL_ENSURE( it
!= m_aDocs
.end(),
346 "OnSaveDone event notified for unknown document!" );
349 else if ( Event
.EventName
== "OnSaveAsDone" )
351 if ( isOfficeDocument( Event
.Source
) )
353 osl::MutexGuard
aGuard( m_aMtx
);
355 uno::Reference
< frame::XModel
>
356 xModel( Event
.Source
, uno::UNO_QUERY
);
357 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
359 DocumentList::iterator it
= m_aDocs
.begin();
360 while ( it
!= m_aDocs
.end() )
362 if ( (*it
).second
.xModel
== xModel
)
364 // Storage gets exchanged while saving.
365 uno::Reference
< document::XStorageBasedDocument
>
366 xDoc( Event
.Source
, uno::UNO_QUERY
);
367 OSL_ENSURE( xDoc
.is(),
368 "Got no document::XStorageBasedDocument!" );
370 uno::Reference
< embed::XStorage
> xStorage
371 = xDoc
->getDocumentStorage();
372 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
374 (*it
).second
.xStorage
= xStorage
;
377 (*it
).second
.aTitle
= DocumentInfo::getDocumentTitle( xModel
);
383 OSL_ENSURE( it
!= m_aDocs
.end(),
384 "OnSaveAsDone event notified for unknown document!" );
387 else if ( Event
.EventName
== "OnTitleChanged" )
389 if ( isOfficeDocument( Event
.Source
) )
391 osl::MutexGuard
aGuard( m_aMtx
);
393 uno::Reference
< frame::XModel
>
394 xModel( Event
.Source
, uno::UNO_QUERY
);
395 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
397 DocumentList::iterator it
= m_aDocs
.begin();
398 while ( it
!= m_aDocs
.end() )
400 if ( (*it
).second
.xModel
== xModel
)
403 rtl:: OUString aTitle
= DocumentInfo::getDocumentTitle( xModel
);
404 (*it
).second
.aTitle
= aTitle
;
407 uno::Reference
< document::XStorageBasedDocument
>
408 xDoc( Event
.Source
, uno::UNO_QUERY
);
409 OSL_ENSURE( xDoc
.is(), "Got no document::XStorageBasedDocument!" );
411 uno::Reference
< embed::XStorage
> xStorage
412 = xDoc
->getDocumentStorage();
413 OSL_ENSURE( xDoc
.is(), "Got no document storage!" );
415 rtl:: OUString aDocId
= getDocumentId( Event
.Source
);
417 m_aDocs
[ aDocId
] = StorageInfo( aTitle
, xStorage
, xModel
);
423 // OSL_ENSURE( it != m_aDocs.end(),
424 // "TitleChanged event notified for unknown document!" );
425 // TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
426 // and the fix is more difficult than what can be done now.
427 // The problem is that at the moment, when you close a SFX-based document via API, it will first
428 // fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
429 // Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
430 // *after* OnUnload and all other On* events.
431 // In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
432 // as last event. When this happens, the assertion here must be enabled, again.
433 // There is no bug for this, yet - IZ is currently down due to the Kenai migration.
434 // 2011-02-23 / frank.schoenheit@sun.com
439 //=========================================================================
441 // lang::XEventListener (base of document::XEventListener)
443 //=========================================================================
446 void SAL_CALL
OfficeDocumentsManager::disposing(
447 const lang::EventObject
& /*Source*/ )
448 throw ( uno::RuntimeException
)
452 //=========================================================================
456 //=========================================================================
459 uno::Reference
< document::XEventBroadcaster
>
460 OfficeDocumentsManager::createDocumentEventNotifier(
461 const uno::Reference
< lang::XMultiServiceFactory
>& rXSMgr
)
463 uno::Reference
< uno::XInterface
> xIfc
;
466 xIfc
= rXSMgr
->createInstance(
468 RTL_CONSTASCII_USTRINGPARAM(
469 "com.sun.star.frame.GlobalEventBroadcaster" ) ) );
471 catch ( uno::Exception
const & )
478 "Could not instanciate com.sun.star.frame.GlobalEventBroadcaster" );
482 uno::Reference
< document::XEventBroadcaster
> xBC(
483 xIfc
, uno::UNO_QUERY
);
487 "com.sun.star.frame.GlobalEventBroadcaster does not implement "
488 "interface com.sun.star.document.XEventBroadcaster!" );
493 return uno::Reference
< document::XEventBroadcaster
>();
496 //=========================================================================
497 void OfficeDocumentsManager::buildDocumentsList()
499 OSL_ENSURE( m_xDocEvtNotifier
.is(),
500 "OfficeDocumentsManager::buildDocumentsList - "
501 "No document event notifier!" );
503 uno::Reference
< container::XEnumerationAccess
> xEnumAccess(
504 m_xDocEvtNotifier
, uno::UNO_QUERY_THROW
);
506 uno::Reference
< container::XEnumeration
> xEnum
507 = xEnumAccess
->createEnumeration();
509 osl::MutexGuard
aGuard( m_aMtx
);
511 while ( xEnum
->hasMoreElements() )
513 uno::Any aValue
= xEnum
->nextElement();
514 // container::NoSuchElementException
515 // lang::WrappedTargetException
519 uno::Reference
< frame::XModel
> xModel
;
524 if ( isOfficeDocument( xModel
) )
526 DocumentList::const_iterator it
= m_aDocs
.begin();
527 while ( it
!= m_aDocs
.end() )
529 if ( (*it
).second
.xModel
== xModel
)
537 if ( it
== m_aDocs
.end() )
540 rtl::OUString aDocId
= getDocumentId( xModel
);
541 rtl::OUString aTitle
= DocumentInfo::getDocumentTitle( xModel
);
543 uno::Reference
< document::XStorageBasedDocument
>
544 xDoc( xModel
, uno::UNO_QUERY
);
545 OSL_ENSURE( xDoc
.is(),
546 "Got no document::XStorageBasedDocument!" );
548 uno::Reference
< embed::XStorage
> xStorage
549 = xDoc
->getDocumentStorage();
550 OSL_ENSURE( xDoc
.is(), "Got no document storage!" );
553 = StorageInfo( aTitle
, xStorage
, xModel
);
555 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
556 xModel
, uno::UNO_QUERY
);
557 OSL_ENSURE( xCloseBroadcaster
.is(),
558 "buildDocumentsList: got no close broadcaster!" );
560 if ( xCloseBroadcaster
.is() )
561 xCloseBroadcaster
->addCloseListener( m_xDocCloseListener
);
566 catch ( lang::DisposedException
const & )
568 // Note: Due to race conditions the XEnumeration can
569 // contains docs that already have been closed
574 //=========================================================================
575 uno::Reference
< embed::XStorage
>
576 OfficeDocumentsManager::queryStorage( const rtl::OUString
& rDocId
)
578 osl::MutexGuard
aGuard( m_aMtx
);
580 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
581 if ( it
== m_aDocs
.end() )
582 return uno::Reference
< embed::XStorage
>();
584 return (*it
).second
.xStorage
;
587 //=========================================================================
588 rtl::OUString
OfficeDocumentsManager::queryDocumentId(
589 const uno::Reference
< frame::XModel
> & xModel
)
591 return getDocumentId( xModel
);
594 //=========================================================================
595 uno::Reference
< frame::XModel
>
596 OfficeDocumentsManager::queryDocumentModel( const rtl::OUString
& rDocId
)
598 osl::MutexGuard
aGuard( m_aMtx
);
600 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
601 if ( it
== m_aDocs
.end() )
602 return uno::Reference
< frame::XModel
>();
604 return (*it
).second
.xModel
;
607 //=========================================================================
608 uno::Sequence
< rtl::OUString
> OfficeDocumentsManager::queryDocuments()
610 osl::MutexGuard
aGuard( m_aMtx
);
612 uno::Sequence
< rtl::OUString
> aRet( m_aDocs
.size() );
615 DocumentList::const_iterator it
= m_aDocs
.begin();
616 while ( it
!= m_aDocs
.end() )
618 aRet
[ nPos
] = (*it
).first
;
625 //=========================================================================
627 OfficeDocumentsManager::queryStorageTitle( const rtl::OUString
& rDocId
)
629 osl::MutexGuard
aGuard( m_aMtx
);
631 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
632 if ( it
== m_aDocs
.end() )
633 return rtl::OUString();
635 return (*it
).second
.aTitle
;
638 //=========================================================================
639 bool OfficeDocumentsManager::isDocumentPreview(
640 const uno::Reference
< frame::XModel
> & xModel
)
645 ::comphelper::NamedValueCollection
aArgs(
647 sal_Bool bIsPreview
= aArgs
.getOrDefault( "Preview", sal_False
);
651 //=========================================================================
652 bool OfficeDocumentsManager::isHelpDocument(
653 const uno::Reference
< frame::XModel
> & xModel
)
658 ::rtl::OUString
sURL( xModel
->getURL() );
659 if ( sURL
.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "vnd.sun.star.help://" ) ) )
665 //=========================================================================
666 bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
667 const uno::Reference
< frame::XModel
> & xModel
)
672 uno::Reference
< frame::XController
> xController
673 = xModel
->getCurrentController();
674 if ( xController
.is() )
676 uno::Reference
< frame::XFrame
> xFrame
677 = xController
->getFrame();
680 // don't use XFrame::isTop here. This nowadays excludes
681 // "sub documents" such as forms embedded in database documents
682 uno::Reference
< awt::XTopWindow
> xFrameContainer(
683 xFrame
->getContainerWindow(), uno::UNO_QUERY
);
684 if ( !xFrameContainer
.is() )
692 //=========================================================================
693 bool OfficeDocumentsManager::isBasicIDE(
694 const uno::Reference
< frame::XModel
> & xModel
)
696 if ( !m_xModuleMgr
.is() )
698 osl::MutexGuard
aGuard( m_aMtx
);
699 if ( !m_xModuleMgr
.is() )
705 frame::XModuleManager
>(
706 m_xSMgr
->createInstance(
708 RTL_CONSTASCII_USTRINGPARAM(
709 "com.sun.star.frame.ModuleManager" ) ) ),
712 catch ( uno::Exception
const & )
717 OSL_ENSURE( m_xModuleMgr
.is(),
718 "Could not instanciate ModuleManager service!" );
722 if ( m_xModuleMgr
.is() )
724 rtl::OUString aModule
;
727 aModule
= m_xModuleMgr
->identify( xModel
);
729 catch ( lang::IllegalArgumentException
const & )
731 OSL_FAIL( "Caught IllegalArgumentException!" );
733 catch ( frame::UnknownModuleException
const & )
735 OSL_FAIL( "Caught UnknownModuleException!" );
738 if ( !aModule
.isEmpty() )
740 // Filter unwanted items, that are no real documents.
741 if ( aModule
== "com.sun.star.script.BasicIDE" )
751 //=========================================================================
752 bool OfficeDocumentsManager::isOfficeDocument(
753 const uno::Reference
< uno::XInterface
> & xDoc
)
755 uno::Reference
< frame::XModel
> xModel( xDoc
, uno::UNO_QUERY
);
756 uno::Reference
< document::XStorageBasedDocument
>
757 xStorageBasedDoc( xModel
, uno::UNO_QUERY
);
758 if ( !xStorageBasedDoc
.is() )
761 if ( !isWithoutOrInTopLevelFrame( xModel
) )
764 if ( isDocumentPreview( xModel
) )
767 if ( isHelpDocument( xModel
) )
770 if ( isBasicIDE( xModel
) )
776 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */