1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 /**************************************************************************
23 **************************************************************************
25 *************************************************************************/
27 #include "osl/diagnose.h"
28 #include "rtl/ref.hxx"
29 #include "cppuhelper/weak.hxx"
31 #include "comphelper/documentinfo.hxx"
32 #include "comphelper/namedvaluecollection.hxx"
34 #include "com/sun/star/awt/XTopWindow.hpp"
35 #include "com/sun/star/beans/XPropertySet.hpp"
36 #include "com/sun/star/document/XDocumentEventBroadcaster.hpp"
37 #include "com/sun/star/document/XStorageBasedDocument.hpp"
38 #include "com/sun/star/frame/theGlobalEventBroadcaster.hpp"
39 #include "com/sun/star/frame/XStorable.hpp"
40 #include "com/sun/star/frame/ModuleManager.hpp"
41 #include "com/sun/star/lang/DisposedException.hpp"
42 #include "com/sun/star/util/XCloseBroadcaster.hpp"
44 #include "tdoc_docmgr.hxx"
46 using namespace com::sun::star
;
47 using namespace tdoc_ucp
;
49 // OfficeDocumentsCloseListener Implementation.
56 // util::XCloseListener
61 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
62 const lang::EventObject
& /*Source*/, sal_Bool
/*GetsOwnership*/ )
63 throw ( util::CloseVetoException
,
64 uno::RuntimeException
, std::exception
)
69 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
70 const lang::EventObject
& Source
)
71 throw ( uno::RuntimeException
, std::exception
)
73 if (!m_pManager
) return; // disposed?
75 document::DocumentEvent aDocEvent
;
76 aDocEvent
.Source
= Source
.Source
;
77 aDocEvent
.EventName
= "OfficeDocumentsListener::notifyClosing";
78 m_pManager
->documentEventOccured( aDocEvent
);
83 // lang::XDocumentEventListener (base of util::XCloseListener)
88 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
89 const lang::EventObject
& /*Source*/ )
90 throw ( uno::RuntimeException
, std::exception
)
97 // OfficeDocumentsManager Implementation.
102 OfficeDocumentsManager::OfficeDocumentsManager(
103 const uno::Reference
< uno::XComponentContext
> & rxContext
,
104 OfficeDocumentsEventListener
* pDocEventListener
)
105 : m_xContext( rxContext
),
106 m_xDocEvtNotifier( frame::theGlobalEventBroadcaster::get( rxContext
) ),
107 m_pDocEventListener( pDocEventListener
),
108 m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
110 // Order is important (multithreaded environment)
111 uno::Reference
< document::XDocumentEventBroadcaster
>(
112 m_xDocEvtNotifier
, uno::UNO_QUERY_THROW
)->addDocumentEventListener( this );
113 buildDocumentsList();
118 OfficeDocumentsManager::~OfficeDocumentsManager()
120 //OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
121 // no need to assert this: Normal shutdown of OOo could already trigger it, since the order in which
122 // objects are actually released/destroyed upon shutdown is not defined. And when we arrive *here*,
123 // OOo *is* shutting down currently, since we're held by the TDOC provider, which is disposed
125 m_xDocCloseListener
->Dispose();
129 void OfficeDocumentsManager::destroy()
131 uno::Reference
< document::XDocumentEventBroadcaster
>(
132 m_xDocEvtNotifier
, uno::UNO_QUERY_THROW
)->removeDocumentEventListener( this );
137 getDocumentId( const uno::Reference
< uno::XInterface
> & xDoc
)
141 // Try to get the UID directly from the document.
142 uno::Reference
< beans::XPropertySet
> xPropSet( xDoc
, uno::UNO_QUERY
);
147 uno::Any aValue
= xPropSet
->getPropertyValue(
148 OUString( "RuntimeUID" ) );
151 catch ( beans::UnknownPropertyException
const & )
153 // Not actually an error. Property is optional.
155 catch ( lang::WrappedTargetException
const & )
157 OSL_FAIL( "Caught WrappedTargetException!" );
163 // fallback: generate UID from document's this pointer.
164 // normalize the interface pointer first. Else, calls with different
165 // interfaces to the same object (say, XFoo and XBar) will produce
167 uno::Reference
< uno::XInterface
> xNormalizedIFace( xDoc
, uno::UNO_QUERY
);
168 sal_Int64 nId
= reinterpret_cast< sal_Int64
>( xNormalizedIFace
.get() );
169 aId
= OUString::number( nId
);
172 OSL_ENSURE( !aId
.isEmpty(), "getDocumentId - Empty id!" );
178 // document::XDocumentEventListener
183 void SAL_CALL
OfficeDocumentsManager::documentEventOccured(
184 const document::DocumentEvent
& Event
)
185 throw ( uno::RuntimeException
, std::exception
)
188 Events documentation: OOo Developer's Guide / Writing UNO Components / Jobs
191 if ( Event
.EventName
== "OnLoadFinished" // document loaded
192 || Event
.EventName
== "OnCreate" ) // document created
194 if ( isOfficeDocument( Event
.Source
) )
196 osl::MutexGuard
aGuard( m_aMtx
);
198 uno::Reference
< frame::XModel
>
199 xModel( Event
.Source
, uno::UNO_QUERY
);
200 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
202 DocumentList::const_iterator it
= m_aDocs
.begin();
203 while ( it
!= m_aDocs
.end() )
205 if ( (*it
).second
.xModel
== xModel
)
213 if ( it
== m_aDocs
.end() )
217 uno::Reference
< document::XStorageBasedDocument
>
218 xDoc( Event
.Source
, uno::UNO_QUERY
);
219 OSL_ENSURE( xDoc
.is(), "Got no document::XStorageBasedDocument!" );
221 uno::Reference
< embed::XStorage
> xStorage
222 = xDoc
->getDocumentStorage();
223 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
225 rtl:: OUString aDocId
= getDocumentId( Event
.Source
);
226 rtl:: OUString aTitle
= comphelper::DocumentInfo::getDocumentTitle(
227 uno::Reference
< frame::XModel
>( Event
.Source
, uno::UNO_QUERY
) );
229 m_aDocs
[ aDocId
] = StorageInfo( aTitle
, xStorage
, xModel
);
231 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
232 Event
.Source
, uno::UNO_QUERY
);
233 OSL_ENSURE( xCloseBroadcaster
.is(),
234 "OnLoadFinished/OnCreate event: got no close broadcaster!" );
236 if ( xCloseBroadcaster
.is() )
237 xCloseBroadcaster
->addCloseListener(m_xDocCloseListener
.get());
239 // Propagate document closure.
240 OSL_ENSURE( m_pDocEventListener
,
241 "OnLoadFinished/OnCreate event: no owner for insert event propagation!" );
243 if ( m_pDocEventListener
)
244 m_pDocEventListener
->notifyDocumentOpened( aDocId
);
248 else if ( Event
.EventName
== "OfficeDocumentsListener::notifyClosing" )
250 if ( isOfficeDocument( Event
.Source
) )
252 // Document has been closed (unloaded)
254 // #163732# - Official event "OnUnload" does not work here. Event
255 // gets fired to early. Other OnUnload listeners called after this
256 // listener may still need TDOC access to the document. Remove the
257 // document from TDOC docs list on XCloseListener::notifyClosing.
258 // See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.
260 osl::MutexGuard
aGuard( m_aMtx
);
262 uno::Reference
< frame::XModel
>
263 xModel( Event
.Source
, uno::UNO_QUERY
);
264 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
266 DocumentList::iterator it
= m_aDocs
.begin();
267 while ( it
!= m_aDocs
.end() )
269 if ( (*it
).second
.xModel
== xModel
)
271 // Propagate document closure.
272 OSL_ENSURE( m_pDocEventListener
,
273 "OnUnload event: no owner for close event propagation!" );
275 if ( m_pDocEventListener
)
277 OUString
aDocId( (*it
).first
);
278 m_pDocEventListener
->notifyDocumentClosed( aDocId
);
285 OSL_ENSURE( it
!= m_aDocs
.end(),
286 "OnUnload event notified for unknown document!" );
288 if ( it
!= m_aDocs
.end() )
290 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
291 Event
.Source
, uno::UNO_QUERY
);
292 OSL_ENSURE( xCloseBroadcaster
.is(),
293 "OnUnload event: got no XCloseBroadcaster from XModel" );
295 if ( xCloseBroadcaster
.is() )
296 xCloseBroadcaster
->removeCloseListener(m_xDocCloseListener
.get());
302 else if ( Event
.EventName
== "OnSaveDone" )
304 if ( isOfficeDocument( Event
.Source
) )
306 osl::MutexGuard
aGuard( m_aMtx
);
308 uno::Reference
< frame::XModel
>
309 xModel( Event
.Source
, uno::UNO_QUERY
);
310 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
312 DocumentList::iterator it
= m_aDocs
.begin();
313 while ( it
!= m_aDocs
.end() )
315 if ( (*it
).second
.xModel
== xModel
)
317 // Storage gets exchanged while saving.
318 uno::Reference
< document::XStorageBasedDocument
>
319 xDoc( Event
.Source
, uno::UNO_QUERY
);
320 OSL_ENSURE( xDoc
.is(),
321 "Got no document::XStorageBasedDocument!" );
323 uno::Reference
< embed::XStorage
> xStorage
324 = xDoc
->getDocumentStorage();
325 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
327 (*it
).second
.xStorage
= xStorage
;
333 OSL_ENSURE( it
!= m_aDocs
.end(),
334 "OnSaveDone event notified for unknown document!" );
337 else if ( Event
.EventName
== "OnSaveAsDone" )
339 if ( isOfficeDocument( Event
.Source
) )
341 osl::MutexGuard
aGuard( m_aMtx
);
343 uno::Reference
< frame::XModel
>
344 xModel( Event
.Source
, uno::UNO_QUERY
);
345 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
347 DocumentList::iterator it
= m_aDocs
.begin();
348 while ( it
!= m_aDocs
.end() )
350 if ( (*it
).second
.xModel
== xModel
)
352 // Storage gets exchanged while saving.
353 uno::Reference
< document::XStorageBasedDocument
>
354 xDoc( Event
.Source
, uno::UNO_QUERY
);
355 OSL_ENSURE( xDoc
.is(),
356 "Got no document::XStorageBasedDocument!" );
358 uno::Reference
< embed::XStorage
> xStorage
359 = xDoc
->getDocumentStorage();
360 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
362 (*it
).second
.xStorage
= xStorage
;
365 (*it
).second
.aTitle
= comphelper::DocumentInfo::getDocumentTitle( xModel
);
371 OSL_ENSURE( it
!= m_aDocs
.end(),
372 "OnSaveAsDone event notified for unknown document!" );
375 else if ( Event
.EventName
== "OnTitleChanged" )
377 if ( isOfficeDocument( Event
.Source
) )
379 osl::MutexGuard
aGuard( m_aMtx
);
381 uno::Reference
< frame::XModel
>
382 xModel( Event
.Source
, uno::UNO_QUERY
);
383 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
385 DocumentList::iterator it
= m_aDocs
.begin();
386 while ( it
!= m_aDocs
.end() )
388 if ( (*it
).second
.xModel
== xModel
)
391 rtl:: OUString aTitle
= comphelper::DocumentInfo::getDocumentTitle( xModel
);
392 (*it
).second
.aTitle
= aTitle
;
395 uno::Reference
< document::XStorageBasedDocument
>
396 xDoc( Event
.Source
, uno::UNO_QUERY
);
397 OSL_ENSURE( xDoc
.is(), "Got no document::XStorageBasedDocument!" );
399 uno::Reference
< embed::XStorage
> xStorage
400 = xDoc
->getDocumentStorage();
401 OSL_ENSURE( xDoc
.is(), "Got no document storage!" );
403 rtl:: OUString aDocId
= getDocumentId( Event
.Source
);
405 m_aDocs
[ aDocId
] = StorageInfo( aTitle
, xStorage
, xModel
);
411 // OSL_ENSURE( it != m_aDocs.end(),
412 // "TitleChanged event notified for unknown document!" );
413 // TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
414 // and the fix is more difficult than what can be done now.
415 // The problem is that at the moment, when you close a SFX-based document via API, it will first
416 // fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
417 // Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
418 // *after* OnUnload and all other On* events.
419 // In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
420 // as last event. When this happens, the assertion here must be enabled, again.
421 // There is no bug for this, yet - IZ is currently down due to the Kenai migration.
422 // 2011-02-23 / frank.schoenheit@sun.com
429 // lang::XDocumentEventListener (base of document::XDocumentEventListener)
434 void SAL_CALL
OfficeDocumentsManager::disposing(
435 const lang::EventObject
& /*Source*/ )
436 throw ( uno::RuntimeException
, std::exception
)
446 void OfficeDocumentsManager::buildDocumentsList()
448 uno::Reference
< container::XEnumeration
> xEnum
449 = m_xDocEvtNotifier
->createEnumeration();
451 osl::MutexGuard
aGuard( m_aMtx
);
453 while ( xEnum
->hasMoreElements() )
455 uno::Any aValue
= xEnum
->nextElement();
456 // container::NoSuchElementException
457 // lang::WrappedTargetException
461 uno::Reference
< frame::XModel
> xModel
;
466 if ( isOfficeDocument( xModel
) )
468 DocumentList::const_iterator it
= m_aDocs
.begin();
469 while ( it
!= m_aDocs
.end() )
471 if ( (*it
).second
.xModel
== xModel
)
479 if ( it
== m_aDocs
.end() )
482 OUString aDocId
= getDocumentId( xModel
);
483 OUString aTitle
= comphelper::DocumentInfo::getDocumentTitle( xModel
);
485 uno::Reference
< document::XStorageBasedDocument
>
486 xDoc( xModel
, uno::UNO_QUERY
);
487 OSL_ENSURE( xDoc
.is(),
488 "Got no document::XStorageBasedDocument!" );
490 uno::Reference
< embed::XStorage
> xStorage
491 = xDoc
->getDocumentStorage();
492 OSL_ENSURE( xDoc
.is(), "Got no document storage!" );
495 = StorageInfo( aTitle
, xStorage
, xModel
);
497 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
498 xModel
, uno::UNO_QUERY
);
499 OSL_ENSURE( xCloseBroadcaster
.is(),
500 "buildDocumentsList: got no close broadcaster!" );
502 if ( xCloseBroadcaster
.is() )
503 xCloseBroadcaster
->addCloseListener(m_xDocCloseListener
.get());
508 catch ( lang::DisposedException
const & )
510 // Note: Due to race conditions the XEnumeration can
511 // contains docs that already have been closed
517 uno::Reference
< embed::XStorage
>
518 OfficeDocumentsManager::queryStorage( const OUString
& rDocId
)
520 osl::MutexGuard
aGuard( m_aMtx
);
522 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
523 if ( it
== m_aDocs
.end() )
524 return uno::Reference
< embed::XStorage
>();
526 return (*it
).second
.xStorage
;
530 OUString
OfficeDocumentsManager::queryDocumentId(
531 const uno::Reference
< frame::XModel
> & xModel
)
533 return getDocumentId( xModel
);
537 uno::Reference
< frame::XModel
>
538 OfficeDocumentsManager::queryDocumentModel( const OUString
& rDocId
)
540 osl::MutexGuard
aGuard( m_aMtx
);
542 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
543 if ( it
== m_aDocs
.end() )
544 return uno::Reference
< frame::XModel
>();
546 return (*it
).second
.xModel
;
550 uno::Sequence
< OUString
> OfficeDocumentsManager::queryDocuments()
552 osl::MutexGuard
aGuard( m_aMtx
);
554 uno::Sequence
< OUString
> aRet( m_aDocs
.size() );
557 DocumentList::const_iterator it
= m_aDocs
.begin();
558 while ( it
!= m_aDocs
.end() )
560 aRet
[ nPos
] = (*it
).first
;
569 OfficeDocumentsManager::queryStorageTitle( const OUString
& rDocId
)
571 osl::MutexGuard
aGuard( m_aMtx
);
573 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
574 if ( it
== m_aDocs
.end() )
577 return (*it
).second
.aTitle
;
581 bool OfficeDocumentsManager::isDocumentPreview(
582 const uno::Reference
< frame::XModel
> & xModel
)
587 ::comphelper::NamedValueCollection
aArgs(
589 bool bIsPreview
= aArgs
.getOrDefault( "Preview", sal_False
);
594 bool OfficeDocumentsManager::isHelpDocument(
595 const uno::Reference
< frame::XModel
> & xModel
)
600 OUString
sURL( xModel
->getURL() );
601 if ( sURL
.match( "vnd.sun.star.help://" ) )
608 bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
609 const uno::Reference
< frame::XModel
> & xModel
)
614 uno::Reference
< frame::XController
> xController
615 = xModel
->getCurrentController();
616 if ( xController
.is() )
618 uno::Reference
< frame::XFrame
> xFrame
619 = xController
->getFrame();
622 // don't use XFrame::isTop here. This nowadays excludes
623 // "sub documents" such as forms embedded in database documents
624 uno::Reference
< awt::XTopWindow
> xFrameContainer(
625 xFrame
->getContainerWindow(), uno::UNO_QUERY
);
626 if ( !xFrameContainer
.is() )
635 bool OfficeDocumentsManager::isBasicIDE(
636 const uno::Reference
< frame::XModel
> & xModel
)
638 if ( !m_xModuleMgr
.is() )
640 osl::MutexGuard
aGuard( m_aMtx
);
641 if ( !m_xModuleMgr
.is() )
645 m_xModuleMgr
= frame::ModuleManager::create( m_xContext
);
647 catch ( uno::Exception
const & )
652 OSL_ENSURE( m_xModuleMgr
.is(),
653 "Could not instanciate ModuleManager service!" );
657 if ( m_xModuleMgr
.is() )
662 aModule
= m_xModuleMgr
->identify( xModel
);
664 catch ( lang::IllegalArgumentException
const & )
666 OSL_FAIL( "Caught IllegalArgumentException!" );
668 catch ( frame::UnknownModuleException
const & )
670 OSL_FAIL( "Caught UnknownModuleException!" );
673 if ( !aModule
.isEmpty() )
675 // Filter unwanted items, that are no real documents.
676 if ( aModule
== "com.sun.star.script.BasicIDE" )
687 bool OfficeDocumentsManager::isOfficeDocument(
688 const uno::Reference
< uno::XInterface
> & xDoc
)
690 uno::Reference
< frame::XModel
> xModel( xDoc
, uno::UNO_QUERY
);
691 uno::Reference
< document::XStorageBasedDocument
>
692 xStorageBasedDoc( xModel
, uno::UNO_QUERY
);
693 if ( !xStorageBasedDoc
.is() )
696 if ( !isWithoutOrInTopLevelFrame( xModel
) )
699 if ( isDocumentPreview( xModel
) )
702 if ( isHelpDocument( xModel
) )
705 if ( isBasicIDE( xModel
) )
711 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */