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/XEventBroadcaster.hpp"
37 #include "com/sun/star/document/XStorageBasedDocument.hpp"
38 #include "com/sun/star/frame/GlobalEventBroadcaster.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
;
48 using ::comphelper::DocumentInfo
;
50 //=========================================================================
51 //=========================================================================
53 // OfficeDocumentsCloseListener Implementation.
55 //=========================================================================
56 //=========================================================================
58 //=========================================================================
60 // util::XCloseListener
62 //=========================================================================
65 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
66 const lang::EventObject
& /*Source*/, sal_Bool
/*GetsOwnership*/ )
67 throw ( util::CloseVetoException
,
68 uno::RuntimeException
)
72 //=========================================================================
73 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
74 const lang::EventObject
& Source
)
75 throw ( uno::RuntimeException
)
77 document::EventObject aDocEvent
;
78 aDocEvent
.Source
= Source
.Source
;
79 aDocEvent
.EventName
= rtl::OUString(
80 RTL_CONSTASCII_USTRINGPARAM( "OfficeDocumentsListener::notifyClosing" ) );
81 m_pManager
->notifyEvent( aDocEvent
);
84 //=========================================================================
86 // lang::XEventListener (base of util::XCloseListener)
88 //=========================================================================
91 void SAL_CALL
OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
92 const lang::EventObject
& /*Source*/ )
93 throw ( uno::RuntimeException
)
97 //=========================================================================
98 //=========================================================================
100 // OfficeDocumentsManager Implementation.
102 //=========================================================================
103 //=========================================================================
105 OfficeDocumentsManager::OfficeDocumentsManager(
106 const uno::Reference
< uno::XComponentContext
> & rxContext
,
107 OfficeDocumentsEventListener
* pDocEventListener
)
108 : m_xContext( rxContext
),
109 m_xDocEvtNotifier( frame::GlobalEventBroadcaster::create( rxContext
) ),
110 m_pDocEventListener( pDocEventListener
),
111 m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
113 // Order is important (multithreaded environment)
114 uno::Reference
< document::XEventBroadcaster
>(
115 m_xDocEvtNotifier
, uno::UNO_QUERY_THROW
)->addEventListener( this );
116 buildDocumentsList();
119 //=========================================================================
121 OfficeDocumentsManager::~OfficeDocumentsManager()
123 //OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
124 // no need to assert this: Normal shutdown of OOo could already trigger it, since the order in which
125 // objects are actually released/destroyed upon shutdown is not defined. And when we arrive *here*,
126 // OOo *is* shutting down currently, since we're held by the TDOC provider, which is disposed
130 //=========================================================================
131 void OfficeDocumentsManager::destroy()
133 uno::Reference
< document::XEventBroadcaster
>(
134 m_xDocEvtNotifier
, uno::UNO_QUERY_THROW
)->removeEventListener( this );
137 //=========================================================================
139 getDocumentId( const uno::Reference
< uno::XInterface
> & xDoc
)
143 // Try to get the UID directly from the document.
144 uno::Reference
< beans::XPropertySet
> xPropSet( xDoc
, uno::UNO_QUERY
);
149 uno::Any aValue
= xPropSet
->getPropertyValue(
151 RTL_CONSTASCII_USTRINGPARAM( "RuntimeUID" ) ) );
154 catch ( beans::UnknownPropertyException
const & )
156 // Not actually an error. Property is optional.
158 catch ( lang::WrappedTargetException
const & )
160 OSL_FAIL( "Caught WrappedTargetException!" );
166 // fallback: generate UID from document's this pointer.
167 // normalize the interface pointer first. Else, calls with different
168 // interfaces to the same object (say, XFoo and XBar) will produce
170 uno::Reference
< uno::XInterface
> xNormalizedIFace( xDoc
, uno::UNO_QUERY
);
171 sal_Int64 nId
= reinterpret_cast< sal_Int64
>( xNormalizedIFace
.get() );
172 aId
= rtl::OUString::valueOf( nId
);
175 OSL_ENSURE( !aId
.isEmpty(), "getDocumentId - Empty id!" );
179 //=========================================================================
181 // document::XEventListener
183 //=========================================================================
186 void SAL_CALL
OfficeDocumentsManager::notifyEvent(
187 const document::EventObject
& Event
)
188 throw ( uno::RuntimeException
)
191 Events documentation: OOo Developer's Guide / Writing UNO Components / Jobs
194 if ( Event
.EventName
== "OnLoadFinished" // document loaded
195 || Event
.EventName
== "OnCreate" ) // document created
197 if ( isOfficeDocument( Event
.Source
) )
199 osl::MutexGuard
aGuard( m_aMtx
);
201 uno::Reference
< frame::XModel
>
202 xModel( Event
.Source
, uno::UNO_QUERY
);
203 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
205 DocumentList::const_iterator it
= m_aDocs
.begin();
206 while ( it
!= m_aDocs
.end() )
208 if ( (*it
).second
.xModel
== xModel
)
216 if ( it
== m_aDocs
.end() )
220 uno::Reference
< document::XStorageBasedDocument
>
221 xDoc( Event
.Source
, uno::UNO_QUERY
);
222 OSL_ENSURE( xDoc
.is(), "Got no document::XStorageBasedDocument!" );
224 uno::Reference
< embed::XStorage
> xStorage
225 = xDoc
->getDocumentStorage();
226 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
228 rtl:: OUString aDocId
= getDocumentId( Event
.Source
);
229 rtl:: OUString aTitle
= DocumentInfo::getDocumentTitle(
230 uno::Reference
< frame::XModel
>( Event
.Source
, uno::UNO_QUERY
) );
232 m_aDocs
[ aDocId
] = StorageInfo( aTitle
, xStorage
, xModel
);
234 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
235 Event
.Source
, uno::UNO_QUERY
);
236 OSL_ENSURE( xCloseBroadcaster
.is(),
237 "OnLoadFinished/OnCreate event: got no close broadcaster!" );
239 if ( xCloseBroadcaster
.is() )
240 xCloseBroadcaster
->addCloseListener( m_xDocCloseListener
);
242 // Propagate document closure.
243 OSL_ENSURE( m_pDocEventListener
,
244 "OnLoadFinished/OnCreate event: no owner for insert event propagation!" );
246 if ( m_pDocEventListener
)
247 m_pDocEventListener
->notifyDocumentOpened( aDocId
);
251 else if ( Event
.EventName
== "OfficeDocumentsListener::notifyClosing" )
253 if ( isOfficeDocument( Event
.Source
) )
255 // Document has been closed (unloaded)
257 // #163732# - Official event "OnUnload" does not work here. Event
258 // gets fired to early. Other OnUnload listeners called after this
259 // listener may still need TDOC access to the document. Remove the
260 // document from TDOC docs list on XCloseListener::notifyClosing.
261 // See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.
263 osl::MutexGuard
aGuard( m_aMtx
);
265 uno::Reference
< frame::XModel
>
266 xModel( Event
.Source
, uno::UNO_QUERY
);
267 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
269 DocumentList::iterator it
= m_aDocs
.begin();
270 while ( it
!= m_aDocs
.end() )
272 if ( (*it
).second
.xModel
== xModel
)
274 // Propagate document closure.
275 OSL_ENSURE( m_pDocEventListener
,
276 "OnUnload event: no owner for close event propagation!" );
278 if ( m_pDocEventListener
)
280 rtl::OUString
aDocId( (*it
).first
);
281 m_pDocEventListener
->notifyDocumentClosed( aDocId
);
288 OSL_ENSURE( it
!= m_aDocs
.end(),
289 "OnUnload event notified for unknown document!" );
291 if ( it
!= m_aDocs
.end() )
293 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
294 Event
.Source
, uno::UNO_QUERY
);
295 OSL_ENSURE( xCloseBroadcaster
.is(),
296 "OnUnload event: got no XCloseBroadcaster from XModel" );
298 if ( xCloseBroadcaster
.is() )
299 xCloseBroadcaster
->removeCloseListener( m_xDocCloseListener
);
305 else if ( Event
.EventName
== "OnSaveDone" )
307 if ( isOfficeDocument( Event
.Source
) )
309 osl::MutexGuard
aGuard( m_aMtx
);
311 uno::Reference
< frame::XModel
>
312 xModel( Event
.Source
, uno::UNO_QUERY
);
313 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
315 DocumentList::iterator it
= m_aDocs
.begin();
316 while ( it
!= m_aDocs
.end() )
318 if ( (*it
).second
.xModel
== xModel
)
320 // Storage gets exchanged while saving.
321 uno::Reference
< document::XStorageBasedDocument
>
322 xDoc( Event
.Source
, uno::UNO_QUERY
);
323 OSL_ENSURE( xDoc
.is(),
324 "Got no document::XStorageBasedDocument!" );
326 uno::Reference
< embed::XStorage
> xStorage
327 = xDoc
->getDocumentStorage();
328 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
330 (*it
).second
.xStorage
= xStorage
;
336 OSL_ENSURE( it
!= m_aDocs
.end(),
337 "OnSaveDone event notified for unknown document!" );
340 else if ( Event
.EventName
== "OnSaveAsDone" )
342 if ( isOfficeDocument( Event
.Source
) )
344 osl::MutexGuard
aGuard( m_aMtx
);
346 uno::Reference
< frame::XModel
>
347 xModel( Event
.Source
, uno::UNO_QUERY
);
348 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
350 DocumentList::iterator it
= m_aDocs
.begin();
351 while ( it
!= m_aDocs
.end() )
353 if ( (*it
).second
.xModel
== xModel
)
355 // Storage gets exchanged while saving.
356 uno::Reference
< document::XStorageBasedDocument
>
357 xDoc( Event
.Source
, uno::UNO_QUERY
);
358 OSL_ENSURE( xDoc
.is(),
359 "Got no document::XStorageBasedDocument!" );
361 uno::Reference
< embed::XStorage
> xStorage
362 = xDoc
->getDocumentStorage();
363 OSL_ENSURE( xStorage
.is(), "Got no document storage!" );
365 (*it
).second
.xStorage
= xStorage
;
368 (*it
).second
.aTitle
= DocumentInfo::getDocumentTitle( xModel
);
374 OSL_ENSURE( it
!= m_aDocs
.end(),
375 "OnSaveAsDone event notified for unknown document!" );
378 else if ( Event
.EventName
== "OnTitleChanged" )
380 if ( isOfficeDocument( Event
.Source
) )
382 osl::MutexGuard
aGuard( m_aMtx
);
384 uno::Reference
< frame::XModel
>
385 xModel( Event
.Source
, uno::UNO_QUERY
);
386 OSL_ENSURE( xModel
.is(), "Got no frame::XModel!" );
388 DocumentList::iterator it
= m_aDocs
.begin();
389 while ( it
!= m_aDocs
.end() )
391 if ( (*it
).second
.xModel
== xModel
)
394 rtl:: OUString aTitle
= DocumentInfo::getDocumentTitle( xModel
);
395 (*it
).second
.aTitle
= aTitle
;
398 uno::Reference
< document::XStorageBasedDocument
>
399 xDoc( Event
.Source
, uno::UNO_QUERY
);
400 OSL_ENSURE( xDoc
.is(), "Got no document::XStorageBasedDocument!" );
402 uno::Reference
< embed::XStorage
> xStorage
403 = xDoc
->getDocumentStorage();
404 OSL_ENSURE( xDoc
.is(), "Got no document storage!" );
406 rtl:: OUString aDocId
= getDocumentId( Event
.Source
);
408 m_aDocs
[ aDocId
] = StorageInfo( aTitle
, xStorage
, xModel
);
414 // OSL_ENSURE( it != m_aDocs.end(),
415 // "TitleChanged event notified for unknown document!" );
416 // TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
417 // and the fix is more difficult than what can be done now.
418 // The problem is that at the moment, when you close a SFX-based document via API, it will first
419 // fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
420 // Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
421 // *after* OnUnload and all other On* events.
422 // In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
423 // as last event. When this happens, the assertion here must be enabled, again.
424 // There is no bug for this, yet - IZ is currently down due to the Kenai migration.
425 // 2011-02-23 / frank.schoenheit@sun.com
430 //=========================================================================
432 // lang::XEventListener (base of document::XEventListener)
434 //=========================================================================
437 void SAL_CALL
OfficeDocumentsManager::disposing(
438 const lang::EventObject
& /*Source*/ )
439 throw ( uno::RuntimeException
)
443 //=========================================================================
447 //=========================================================================
449 void OfficeDocumentsManager::buildDocumentsList()
451 uno::Reference
< container::XEnumeration
> xEnum
452 = m_xDocEvtNotifier
->createEnumeration();
454 osl::MutexGuard
aGuard( m_aMtx
);
456 while ( xEnum
->hasMoreElements() )
458 uno::Any aValue
= xEnum
->nextElement();
459 // container::NoSuchElementException
460 // lang::WrappedTargetException
464 uno::Reference
< frame::XModel
> xModel
;
469 if ( isOfficeDocument( xModel
) )
471 DocumentList::const_iterator it
= m_aDocs
.begin();
472 while ( it
!= m_aDocs
.end() )
474 if ( (*it
).second
.xModel
== xModel
)
482 if ( it
== m_aDocs
.end() )
485 rtl::OUString aDocId
= getDocumentId( xModel
);
486 rtl::OUString aTitle
= DocumentInfo::getDocumentTitle( xModel
);
488 uno::Reference
< document::XStorageBasedDocument
>
489 xDoc( xModel
, uno::UNO_QUERY
);
490 OSL_ENSURE( xDoc
.is(),
491 "Got no document::XStorageBasedDocument!" );
493 uno::Reference
< embed::XStorage
> xStorage
494 = xDoc
->getDocumentStorage();
495 OSL_ENSURE( xDoc
.is(), "Got no document storage!" );
498 = StorageInfo( aTitle
, xStorage
, xModel
);
500 uno::Reference
< util::XCloseBroadcaster
> xCloseBroadcaster(
501 xModel
, uno::UNO_QUERY
);
502 OSL_ENSURE( xCloseBroadcaster
.is(),
503 "buildDocumentsList: got no close broadcaster!" );
505 if ( xCloseBroadcaster
.is() )
506 xCloseBroadcaster
->addCloseListener( m_xDocCloseListener
);
511 catch ( lang::DisposedException
const & )
513 // Note: Due to race conditions the XEnumeration can
514 // contains docs that already have been closed
519 //=========================================================================
520 uno::Reference
< embed::XStorage
>
521 OfficeDocumentsManager::queryStorage( const rtl::OUString
& rDocId
)
523 osl::MutexGuard
aGuard( m_aMtx
);
525 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
526 if ( it
== m_aDocs
.end() )
527 return uno::Reference
< embed::XStorage
>();
529 return (*it
).second
.xStorage
;
532 //=========================================================================
533 rtl::OUString
OfficeDocumentsManager::queryDocumentId(
534 const uno::Reference
< frame::XModel
> & xModel
)
536 return getDocumentId( xModel
);
539 //=========================================================================
540 uno::Reference
< frame::XModel
>
541 OfficeDocumentsManager::queryDocumentModel( const rtl::OUString
& rDocId
)
543 osl::MutexGuard
aGuard( m_aMtx
);
545 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
546 if ( it
== m_aDocs
.end() )
547 return uno::Reference
< frame::XModel
>();
549 return (*it
).second
.xModel
;
552 //=========================================================================
553 uno::Sequence
< rtl::OUString
> OfficeDocumentsManager::queryDocuments()
555 osl::MutexGuard
aGuard( m_aMtx
);
557 uno::Sequence
< rtl::OUString
> aRet( m_aDocs
.size() );
560 DocumentList::const_iterator it
= m_aDocs
.begin();
561 while ( it
!= m_aDocs
.end() )
563 aRet
[ nPos
] = (*it
).first
;
570 //=========================================================================
572 OfficeDocumentsManager::queryStorageTitle( const rtl::OUString
& rDocId
)
574 osl::MutexGuard
aGuard( m_aMtx
);
576 DocumentList::const_iterator it
= m_aDocs
.find( rDocId
);
577 if ( it
== m_aDocs
.end() )
578 return rtl::OUString();
580 return (*it
).second
.aTitle
;
583 //=========================================================================
584 bool OfficeDocumentsManager::isDocumentPreview(
585 const uno::Reference
< frame::XModel
> & xModel
)
590 ::comphelper::NamedValueCollection
aArgs(
592 sal_Bool bIsPreview
= aArgs
.getOrDefault( "Preview", sal_False
);
596 //=========================================================================
597 bool OfficeDocumentsManager::isHelpDocument(
598 const uno::Reference
< frame::XModel
> & xModel
)
603 ::rtl::OUString
sURL( xModel
->getURL() );
604 if ( sURL
.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "vnd.sun.star.help://" ) ) )
610 //=========================================================================
611 bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
612 const uno::Reference
< frame::XModel
> & xModel
)
617 uno::Reference
< frame::XController
> xController
618 = xModel
->getCurrentController();
619 if ( xController
.is() )
621 uno::Reference
< frame::XFrame
> xFrame
622 = xController
->getFrame();
625 // don't use XFrame::isTop here. This nowadays excludes
626 // "sub documents" such as forms embedded in database documents
627 uno::Reference
< awt::XTopWindow
> xFrameContainer(
628 xFrame
->getContainerWindow(), uno::UNO_QUERY
);
629 if ( !xFrameContainer
.is() )
637 //=========================================================================
638 bool OfficeDocumentsManager::isBasicIDE(
639 const uno::Reference
< frame::XModel
> & xModel
)
641 if ( !m_xModuleMgr
.is() )
643 osl::MutexGuard
aGuard( m_aMtx
);
644 if ( !m_xModuleMgr
.is() )
648 m_xModuleMgr
= frame::ModuleManager::create( m_xContext
);
650 catch ( uno::Exception
const & )
655 OSL_ENSURE( m_xModuleMgr
.is(),
656 "Could not instanciate ModuleManager service!" );
660 if ( m_xModuleMgr
.is() )
662 rtl::OUString aModule
;
665 aModule
= m_xModuleMgr
->identify( xModel
);
667 catch ( lang::IllegalArgumentException
const & )
669 OSL_FAIL( "Caught IllegalArgumentException!" );
671 catch ( frame::UnknownModuleException
const & )
673 OSL_FAIL( "Caught UnknownModuleException!" );
676 if ( !aModule
.isEmpty() )
678 // Filter unwanted items, that are no real documents.
679 if ( aModule
== "com.sun.star.script.BasicIDE" )
689 //=========================================================================
690 bool OfficeDocumentsManager::isOfficeDocument(
691 const uno::Reference
< uno::XInterface
> & xDoc
)
693 uno::Reference
< frame::XModel
> xModel( xDoc
, uno::UNO_QUERY
);
694 uno::Reference
< document::XStorageBasedDocument
>
695 xStorageBasedDoc( xModel
, uno::UNO_QUERY
);
696 if ( !xStorageBasedDoc
.is() )
699 if ( !isWithoutOrInTopLevelFrame( xModel
) )
702 if ( isDocumentPreview( xModel
) )
705 if ( isHelpDocument( xModel
) )
708 if ( isBasicIDE( xModel
) )
714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */