bump product version to 7.2.5.1
[LibreOffice.git] / ucb / source / ucp / tdoc / tdoc_docmgr.cxx
blobaf66cb8a4b251fee7b5a41ad77ac9a096178926e
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 #include <rtl/ref.hxx>
22 #include <tools/diagnose_ex.h>
24 #include <comphelper/documentinfo.hxx>
25 #include <comphelper/namedvaluecollection.hxx>
26 #include <comphelper/sequence.hxx>
28 #include <com/sun/star/awt/XTopWindow.hpp>
29 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
31 #include <com/sun/star/document/XStorageBasedDocument.hpp>
32 #include <com/sun/star/frame/UnknownModuleException.hpp>
33 #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
34 #include <com/sun/star/frame/ModuleManager.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/lang/NotInitializedException.hpp>
37 #include <com/sun/star/util/XCloseBroadcaster.hpp>
39 #include "tdoc_docmgr.hxx"
40 #include "tdoc_provider.hxx"
42 using namespace com::sun::star;
43 using namespace tdoc_ucp;
45 // OfficeDocumentsCloseListener Implementation.
48 // util::XCloseListener
51 // virtual
52 void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
53 const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ )
58 void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
59 const lang::EventObject& Source )
61 if (!m_pManager) return; // disposed?
63 document::DocumentEvent aDocEvent;
64 aDocEvent.Source = Source.Source;
65 aDocEvent.EventName = "OfficeDocumentsListener::notifyClosing";
66 m_pManager->documentEventOccured( aDocEvent );
70 // lang::XDocumentEventListener (base of util::XCloseListener)
73 // virtual
74 void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
75 const lang::EventObject& /*Source*/ )
80 // OfficeDocumentsManager Implementation.
83 OfficeDocumentsManager::OfficeDocumentsManager(
84 const uno::Reference< uno::XComponentContext > & rxContext,
85 ContentProvider * pDocEventListener )
86 : m_xContext( rxContext ),
87 m_xDocEvtNotifier( frame::theGlobalEventBroadcaster::get( rxContext ) ),
88 m_pDocEventListener( pDocEventListener ),
89 m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
91 // Order is important (multithreaded environment)
92 uno::Reference< document::XDocumentEventBroadcaster >(
93 m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->addDocumentEventListener( this );
94 buildDocumentsList();
98 // virtual
99 OfficeDocumentsManager::~OfficeDocumentsManager()
101 //OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
102 // no need to assert this: Normal shutdown of LibreOffice could already trigger it, since the order
103 // in which objects are actually released/destroyed upon shutdown is not defined. And when we
104 // arrive *here*, LibreOffice *is* shutting down currently, since we're held by the TDOC provider,
105 // which is disposed upon shutdown.
106 m_xDocCloseListener->Dispose();
110 void OfficeDocumentsManager::destroy()
112 uno::Reference< document::XDocumentEventBroadcaster >(
113 m_xDocEvtNotifier, uno::UNO_QUERY_THROW )->removeDocumentEventListener( this );
117 static OUString
118 getDocumentId( const uno::Reference< uno::XInterface > & xDoc )
120 OUString aId;
122 // Try to get the UID directly from the document.
123 uno::Reference< beans::XPropertySet > xPropSet( xDoc, uno::UNO_QUERY );
124 if ( xPropSet.is() )
128 uno::Any aValue = xPropSet->getPropertyValue("RuntimeUID");
129 aValue >>= aId;
131 catch ( beans::UnknownPropertyException const & )
133 // Not actually an error. Property is optional.
135 catch ( lang::WrappedTargetException const & )
137 TOOLS_WARN_EXCEPTION("ucb.ucp", "Caught WrappedTargetException!");
141 if ( aId.isEmpty() )
143 // fallback: generate UID from document's this pointer.
144 // normalize the interface pointer first. Else, calls with different
145 // interfaces to the same object (say, XFoo and XBar) will produce
146 // different IDs
147 uno::Reference< uno::XInterface > xNormalizedIFace( xDoc, uno::UNO_QUERY );
148 sal_Int64 nId = reinterpret_cast< sal_Int64 >( xNormalizedIFace.get() );
149 aId = OUString::number( nId );
152 OSL_ENSURE( !aId.isEmpty(), "getDocumentId - Empty id!" );
153 return aId;
157 // document::XDocumentEventListener
160 // virtual
161 void SAL_CALL OfficeDocumentsManager::documentEventOccured(
162 const document::DocumentEvent & Event )
165 Events documentation: OOo Developer's Guide / Writing UNO Components /
166 Integrating Components into OpenOffice.org / Jobs
169 if ( Event.EventName == "OnLoadFinished" // document loaded
170 || Event.EventName == "OnCreate" ) // document created
172 if ( isOfficeDocument( Event.Source ) )
174 uno::Reference<frame::XModel> const xModel(
175 Event.Source, uno::UNO_QUERY );
176 OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
178 bool found(false);
181 osl::MutexGuard aGuard( m_aMtx );
183 found = std::any_of(m_aDocs.begin(), m_aDocs.end(),
184 [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
187 if (!found)
189 // no mutex to avoid deadlocks!
190 // need no lock to access const members, ContentProvider is safe
192 // new document
194 uno::Reference< document::XStorageBasedDocument >
195 xDoc( Event.Source, uno::UNO_QUERY );
196 OSL_ENSURE( xDoc.is(), "Got no document::XStorageBasedDocument!" );
198 uno::Reference< embed::XStorage > xStorage
199 = xDoc->getDocumentStorage();
200 OSL_ENSURE( xStorage.is(), "Got no document storage!" );
202 rtl:: OUString aDocId = getDocumentId( Event.Source );
203 rtl:: OUString aTitle = comphelper::DocumentInfo::getDocumentTitle(
204 uno::Reference< frame::XModel >( Event.Source, uno::UNO_QUERY ) );
207 osl::MutexGuard g(m_aMtx);
208 m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
211 uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
212 Event.Source, uno::UNO_QUERY );
213 OSL_ENSURE( xCloseBroadcaster.is(),
214 "OnLoadFinished/OnCreate event: got no close broadcaster!" );
216 if ( xCloseBroadcaster.is() )
217 xCloseBroadcaster->addCloseListener(m_xDocCloseListener);
219 // Propagate document closure.
220 OSL_ENSURE( m_pDocEventListener,
221 "OnLoadFinished/OnCreate event: no owner for insert event propagation!" );
223 if ( m_pDocEventListener )
224 m_pDocEventListener->notifyDocumentOpened( aDocId );
228 else if ( Event.EventName == "OfficeDocumentsListener::notifyClosing" )
230 if ( isOfficeDocument( Event.Source ) )
232 // Document has been closed (unloaded)
234 // Official event "OnUnload" does not work here. Event
235 // gets fired too early. Other OnUnload listeners called after this
236 // listener may still need TDOC access to the document. Remove the
237 // document from TDOC docs list on XCloseListener::notifyClosing.
238 // See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.
240 uno::Reference< frame::XModel >
241 xModel( Event.Source, uno::UNO_QUERY );
242 OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
244 bool found(false);
245 OUString aDocId;
248 osl::MutexGuard aGuard( m_aMtx );
250 auto it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
251 [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
252 if ( it != m_aDocs.end() )
254 aDocId = (*it).first;
255 found = true;
256 m_aDocs.erase( it );
260 OSL_ENSURE( found,
261 "OnUnload event notified for unknown document!" );
263 if (found)
265 // Propagate document closure.
266 OSL_ENSURE( m_pDocEventListener,
267 "OnUnload event: no owner for close event propagation!" );
268 if (m_pDocEventListener)
270 m_pDocEventListener->notifyDocumentClosed(aDocId);
272 uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
273 Event.Source, uno::UNO_QUERY );
274 OSL_ENSURE( xCloseBroadcaster.is(),
275 "OnUnload event: got no XCloseBroadcaster from XModel" );
276 if ( xCloseBroadcaster.is() )
277 xCloseBroadcaster->removeCloseListener(m_xDocCloseListener);
281 else if ( Event.EventName == "OnSaveDone" )
283 if ( isOfficeDocument( Event.Source ) )
285 // Storage gets exchanged while saving.
286 uno::Reference<document::XStorageBasedDocument> const xDoc(
287 Event.Source, uno::UNO_QUERY );
288 OSL_ENSURE( xDoc.is(),
289 "Got no document::XStorageBasedDocument!" );
290 uno::Reference<embed::XStorage> const xStorage(
291 xDoc->getDocumentStorage());
292 OSL_ENSURE( xStorage.is(), "Got no document storage!" );
294 uno::Reference< frame::XModel >
295 xModel( Event.Source, uno::UNO_QUERY );
296 OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
298 osl::MutexGuard aGuard( m_aMtx );
300 DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
301 [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
303 OSL_ENSURE( it != m_aDocs.end(),
304 "OnSaveDone event notified for unknown document!" );
305 if ( it != m_aDocs.end() )
307 (*it).second.xStorage = xStorage;
311 else if ( Event.EventName == "OnSaveAsDone" )
313 if ( isOfficeDocument( Event.Source ) )
315 // Storage gets exchanged while saving.
316 uno::Reference<document::XStorageBasedDocument> const xDoc(
317 Event.Source, uno::UNO_QUERY );
318 OSL_ENSURE( xDoc.is(),
319 "Got no document::XStorageBasedDocument!" );
320 uno::Reference<embed::XStorage> const xStorage(
321 xDoc->getDocumentStorage());
322 OSL_ENSURE( xStorage.is(), "Got no document storage!" );
324 uno::Reference< frame::XModel >
325 xModel( Event.Source, uno::UNO_QUERY );
326 OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
328 OUString const title(comphelper::DocumentInfo::getDocumentTitle(xModel));
330 osl::MutexGuard aGuard( m_aMtx );
332 DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
333 [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
335 OSL_ENSURE( it != m_aDocs.end(),
336 "OnSaveAsDone event notified for unknown document!" );
337 if ( it != m_aDocs.end() )
339 (*it).second.xStorage = xStorage;
341 // Adjust title.
342 (*it).second.aTitle = title;
346 else if ( Event.EventName == "OnTitleChanged"
347 || Event.EventName == "OnStorageChanged" )
349 if ( isOfficeDocument( Event.Source ) )
351 // Storage gets exchanged while saving.
352 uno::Reference<document::XStorageBasedDocument> const xDoc(
353 Event.Source, uno::UNO_QUERY );
354 OSL_ENSURE( xDoc.is(),
355 "Got no document::XStorageBasedDocument!" );
356 uno::Reference<embed::XStorage> const xStorage(
357 xDoc->getDocumentStorage());
358 OSL_ENSURE( xStorage.is(), "Got no document storage!" );
360 uno::Reference< frame::XModel >
361 xModel( Event.Source, uno::UNO_QUERY );
362 OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
364 OUString const aTitle(comphelper::DocumentInfo::getDocumentTitle(xModel));
366 OUString const aDocId(getDocumentId(Event.Source));
368 osl::MutexGuard aGuard( m_aMtx );
370 DocumentList::iterator it = std::find_if(m_aDocs.begin(), m_aDocs.end(),
371 [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
372 if ( it != m_aDocs.end() )
374 // Adjust title.
375 (*it).second.aTitle = aTitle;
377 m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
380 // OSL_ENSURE( it != m_aDocs.end(),
381 // "TitleChanged event notified for unknown document!" );
382 // TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
383 // and the fix is more difficult than what can be done now.
384 // The problem is that at the moment, when you close a SFX-based document via API, it will first
385 // fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
386 // Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
387 // *after* OnUnload and all other On* events.
388 // In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
389 // as last event. When this happens, the assertion here must be enabled, again.
394 // lang::XDocumentEventListener (base of document::XDocumentEventListener)
396 // virtual
397 void SAL_CALL OfficeDocumentsManager::disposing(
398 const lang::EventObject& /*Source*/ )
402 // Non-interface.
404 void OfficeDocumentsManager::buildDocumentsList()
406 uno::Reference< container::XEnumeration > xEnum
407 = m_xDocEvtNotifier->createEnumeration();
409 while ( xEnum->hasMoreElements() )
411 uno::Any aValue = xEnum->nextElement();
412 // container::NoSuchElementException
413 // lang::WrappedTargetException
417 uno::Reference< frame::XModel > xModel;
418 aValue >>= xModel;
420 if ( xModel.is() )
422 if ( isOfficeDocument( xModel ) )
424 bool found(false);
427 osl::MutexGuard aGuard( m_aMtx );
429 found = std::any_of(m_aDocs.begin(), m_aDocs.end(),
430 [&xModel](const DocumentList::value_type& rEntry) { return rEntry.second.xModel == xModel; });
433 if (!found)
435 // new document
436 OUString aDocId = getDocumentId( xModel );
437 OUString aTitle = comphelper::DocumentInfo::getDocumentTitle( xModel );
439 uno::Reference< document::XStorageBasedDocument >
440 xDoc( xModel, uno::UNO_QUERY );
441 OSL_ENSURE( xDoc.is(),
442 "Got no document::XStorageBasedDocument!" );
444 uno::Reference< embed::XStorage > xStorage
445 = xDoc->getDocumentStorage();
446 OSL_ENSURE( xStorage.is(), "Got no document storage!" );
449 osl::MutexGuard aGuard( m_aMtx );
450 m_aDocs[ aDocId ]
451 = StorageInfo( aTitle, xStorage, xModel );
454 uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
455 xModel, uno::UNO_QUERY );
456 OSL_ENSURE( xCloseBroadcaster.is(),
457 "buildDocumentsList: got no close broadcaster!" );
459 if ( xCloseBroadcaster.is() )
460 xCloseBroadcaster->addCloseListener(m_xDocCloseListener);
465 catch ( lang::DisposedException const & )
467 // Note: Due to race conditions the XEnumeration can
468 // contain docs that have already been closed
470 catch ( lang::NotInitializedException const & )
472 // Note: Due to race conditions the XEnumeration can
473 // contain docs that are still uninitialized
478 uno::Reference< embed::XStorage >
479 OfficeDocumentsManager::queryStorage( const OUString & rDocId )
481 osl::MutexGuard aGuard( m_aMtx );
483 DocumentList::const_iterator it = m_aDocs.find( rDocId );
484 if ( it == m_aDocs.end() )
485 return uno::Reference< embed::XStorage >();
487 return (*it).second.xStorage;
491 OUString OfficeDocumentsManager::queryDocumentId(
492 const uno::Reference< frame::XModel > & xModel )
494 return getDocumentId( xModel );
498 uno::Reference< frame::XModel >
499 OfficeDocumentsManager::queryDocumentModel( const OUString & rDocId )
501 osl::MutexGuard aGuard( m_aMtx );
503 DocumentList::const_iterator it = m_aDocs.find( rDocId );
504 if ( it == m_aDocs.end() )
505 return uno::Reference< frame::XModel >();
507 return (*it).second.xModel;
511 uno::Sequence< OUString > OfficeDocumentsManager::queryDocuments()
513 osl::MutexGuard aGuard( m_aMtx );
515 return comphelper::mapKeysToSequence( m_aDocs );
519 OUString
520 OfficeDocumentsManager::queryStorageTitle( const OUString & rDocId )
522 osl::MutexGuard aGuard( m_aMtx );
524 DocumentList::const_iterator it = m_aDocs.find( rDocId );
525 if ( it == m_aDocs.end() )
526 return OUString();
528 return (*it).second.aTitle;
532 bool OfficeDocumentsManager::isDocumentPreview(
533 const uno::Reference< frame::XModel > & xModel )
535 if ( !xModel.is() )
536 return false;
538 ::comphelper::NamedValueCollection aArgs(
539 xModel->getArgs() );
540 bool bIsPreview = aArgs.getOrDefault( "Preview", false );
541 return bIsPreview;
545 bool OfficeDocumentsManager::isHelpDocument(
546 const uno::Reference< frame::XModel > & xModel )
548 if ( !xModel.is() )
549 return false;
551 OUString sURL( xModel->getURL() );
552 return sURL.match( "vnd.sun.star.help://" );
556 bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
557 const uno::Reference< frame::XModel > & xModel )
559 if ( !xModel.is() )
560 return false;
562 uno::Reference< frame::XController > xController
563 = xModel->getCurrentController();
564 if ( xController.is() )
566 uno::Reference< frame::XFrame > xFrame
567 = xController->getFrame();
568 if ( xFrame.is() )
570 // don't use XFrame::isTop here. This nowadays excludes
571 // "sub documents" such as forms embedded in database documents
572 uno::Reference< awt::XTopWindow > xFrameContainer(
573 xFrame->getContainerWindow(), uno::UNO_QUERY );
574 if ( !xFrameContainer.is() )
575 return false;
579 return true;
583 bool OfficeDocumentsManager::isBasicIDE(
584 const uno::Reference< frame::XModel > & xModel )
586 if ( !m_xModuleMgr.is() )
588 osl::MutexGuard aGuard( m_aMtx );
589 if ( !m_xModuleMgr.is() )
593 m_xModuleMgr = frame::ModuleManager::create( m_xContext );
595 catch ( uno::Exception const & )
597 // handled below.
600 OSL_ENSURE( m_xModuleMgr .is(),
601 "Could not instantiate ModuleManager service!" );
605 if ( m_xModuleMgr.is() )
607 OUString aModule;
610 aModule = m_xModuleMgr->identify( xModel );
612 catch ( lang::IllegalArgumentException const & )
614 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
616 catch ( frame::UnknownModuleException const & )
618 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
621 if ( !aModule.isEmpty() )
623 // Filter unwanted items, that are no real documents.
624 if ( aModule == "com.sun.star.script.BasicIDE" )
626 return true;
631 return false;
635 bool OfficeDocumentsManager::isOfficeDocument(
636 const uno::Reference< uno::XInterface > & xDoc )
638 uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
639 uno::Reference< document::XStorageBasedDocument >
640 xStorageBasedDoc( xModel, uno::UNO_QUERY );
641 if ( !xStorageBasedDoc.is() )
642 return false;
644 if ( !isWithoutOrInTopLevelFrame( xModel ) )
645 return false;
647 if ( isDocumentPreview( xModel ) )
648 return false;
650 if ( isHelpDocument( xModel ) )
651 return false;
653 if ( isBasicIDE( xModel ) )
654 return false;
656 return true;
659 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */