Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / ucb / source / ucp / tdoc / tdoc_docmgr.cxx
blob19f35caaa34949ef2a5672a9e299959dc7455457
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 /**************************************************************************
22 TODO
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 //=========================================================================
64 // virtual
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 //=========================================================================
90 // virtual
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 //=========================================================================
120 // virtual
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
127 // upon shutdown.
130 //=========================================================================
131 void OfficeDocumentsManager::destroy()
133 uno::Reference< document::XEventBroadcaster >(
134 m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->removeEventListener( this );
137 //=========================================================================
138 static rtl::OUString
139 getDocumentId( const uno::Reference< uno::XInterface > & xDoc )
141 rtl::OUString aId;
143 // Try to get the UID directly from the document.
144 uno::Reference< beans::XPropertySet > xPropSet( xDoc, uno::UNO_QUERY );
145 if ( xPropSet.is() )
149 uno::Any aValue = xPropSet->getPropertyValue(
150 rtl::OUString(
151 RTL_CONSTASCII_USTRINGPARAM( "RuntimeUID" ) ) );
152 aValue >>= aId;
154 catch ( beans::UnknownPropertyException const & )
156 // Not actually an error. Property is optional.
158 catch ( lang::WrappedTargetException const & )
160 OSL_FAIL( "Caught WrappedTargetException!" );
164 if ( aId.isEmpty() )
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
169 // different IDs
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!" );
176 return aId;
179 //=========================================================================
181 // document::XEventListener
183 //=========================================================================
185 // virtual
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 )
210 // already known.
211 break;
213 ++it;
216 if ( it == m_aDocs.end() )
218 // new document
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 );
283 break;
285 ++it;
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 );
301 m_aDocs.erase( it );
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;
331 break;
333 ++it;
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;
367 // Adjust title.
368 (*it).second.aTitle = DocumentInfo::getDocumentTitle( xModel );
369 break;
371 ++it;
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 )
393 // Adjust title.
394 rtl:: OUString aTitle = DocumentInfo::getDocumentTitle( xModel );
395 (*it).second.aTitle = aTitle;
397 // Adjust storage.
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 );
409 break;
411 ++it;
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 //=========================================================================
436 // virtual
437 void SAL_CALL OfficeDocumentsManager::disposing(
438 const lang::EventObject& /*Source*/ )
439 throw ( uno::RuntimeException )
443 //=========================================================================
445 // Non-interface.
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;
465 aValue >>= xModel;
467 if ( xModel.is() )
469 if ( isOfficeDocument( xModel ) )
471 DocumentList::const_iterator it = m_aDocs.begin();
472 while ( it != m_aDocs.end() )
474 if ( (*it).second.xModel == xModel )
476 // already known.
477 break;
479 ++it;
482 if ( it == m_aDocs.end() )
484 // new document
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!" );
497 m_aDocs[ aDocId ]
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() );
558 sal_Int32 nPos = 0;
560 DocumentList::const_iterator it = m_aDocs.begin();
561 while ( it != m_aDocs.end() )
563 aRet[ nPos ] = (*it).first;
564 ++it;
565 ++nPos;
567 return aRet;
570 //=========================================================================
571 rtl::OUString
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 )
587 if ( !xModel.is() )
588 return false;
590 ::comphelper::NamedValueCollection aArgs(
591 xModel->getArgs() );
592 sal_Bool bIsPreview = aArgs.getOrDefault( "Preview", sal_False );
593 return bIsPreview;
596 //=========================================================================
597 bool OfficeDocumentsManager::isHelpDocument(
598 const uno::Reference< frame::XModel > & xModel )
600 if ( !xModel.is() )
601 return false;
603 ::rtl::OUString sURL( xModel->getURL() );
604 if ( sURL.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "vnd.sun.star.help://" ) ) )
605 return true;
607 return false;
610 //=========================================================================
611 bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
612 const uno::Reference< frame::XModel > & xModel )
614 if ( !xModel.is() )
615 return false;
617 uno::Reference< frame::XController > xController
618 = xModel->getCurrentController();
619 if ( xController.is() )
621 uno::Reference< frame::XFrame > xFrame
622 = xController->getFrame();
623 if ( xFrame.is() )
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() )
630 return false;
634 return true;
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 & )
652 // handled below.
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" )
681 return true;
686 return false;
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() )
697 return false;
699 if ( !isWithoutOrInTopLevelFrame( xModel ) )
700 return false;
702 if ( isDocumentPreview( xModel ) )
703 return false;
705 if ( isHelpDocument( xModel ) )
706 return false;
708 if ( isBasicIDE( xModel ) )
709 return false;
711 return true;
714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */