blob: ffb48bb66ce25b73d1032c3737c4f5f971266d0c [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"
/**************************************************************************
TODO
**************************************************************************
*************************************************************************/
#include "osl/diagnose.h"
#include "rtl/ref.hxx"
#include "cppuhelper/weak.hxx"
#include "comphelper/namedvaluecollection.hxx"
#include "comphelper/documentinfo.hxx"
#include "com/sun/star/awt/XTopWindow.hpp"
#include "com/sun/star/beans/XPropertySet.hpp"
#include "com/sun/star/container/XEnumerationAccess.hpp"
#include "com/sun/star/document/XStorageBasedDocument.hpp"
#include "com/sun/star/frame/XStorable.hpp"
#include "com/sun/star/lang/DisposedException.hpp"
#include "com/sun/star/util/XCloseBroadcaster.hpp"
#include "tdoc_docmgr.hxx"
using namespace com::sun::star;
using namespace tdoc_ucp;
using ::comphelper::DocumentInfo;
//=========================================================================
//=========================================================================
//
// OfficeDocumentsCloseListener Implementation.
//
//=========================================================================
//=========================================================================
//=========================================================================
//
// util::XCloseListener
//
//=========================================================================
// virtual
void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::queryClosing(
const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ )
throw ( util::CloseVetoException,
uno::RuntimeException )
{
}
//=========================================================================
void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::notifyClosing(
const lang::EventObject& Source )
throw ( uno::RuntimeException )
{
document::EventObject aDocEvent;
aDocEvent.Source = Source.Source;
aDocEvent.EventName = rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM( "OfficeDocumentsListener::notifyClosing" ) );
m_pManager->notifyEvent( aDocEvent );
}
//=========================================================================
//
// lang::XEventListener (base of util::XCloseListener)
//
//=========================================================================
// virtual
void SAL_CALL OfficeDocumentsManager::OfficeDocumentsCloseListener::disposing(
const lang::EventObject& /*Source*/ )
throw ( uno::RuntimeException )
{
}
//=========================================================================
//=========================================================================
//
// OfficeDocumentsManager Implementation.
//
//=========================================================================
//=========================================================================
OfficeDocumentsManager::OfficeDocumentsManager(
const uno::Reference< lang::XMultiServiceFactory > & xSMgr,
OfficeDocumentsEventListener * pDocEventListener )
: m_xSMgr( xSMgr ),
m_xDocEvtNotifier( createDocumentEventNotifier( xSMgr ) ),
m_pDocEventListener( pDocEventListener ),
m_xDocCloseListener( new OfficeDocumentsCloseListener( this ) )
{
if ( m_xDocEvtNotifier.is() )
{
// Order is important (multithreaded environment)
m_xDocEvtNotifier->addEventListener( this );
buildDocumentsList();
}
}
//=========================================================================
// virtual
OfficeDocumentsManager::~OfficeDocumentsManager()
{
//OSL_ENSURE( m_aDocs.empty(), "document list not empty!" );
// no need to assert this: Normal shutdown of OOo could already trigger it, since the order in which
// objects are actually released/destroyed upon shutdown is not defined. And when we arrive *here*,
// OOo *is* shutting down currently, since we're held by the TDOC provider, which is disposed
// upon shutdown.
}
//=========================================================================
void OfficeDocumentsManager::destroy()
{
if ( m_xDocEvtNotifier.is() )
m_xDocEvtNotifier->removeEventListener( this );
}
//=========================================================================
static rtl::OUString
getDocumentId( const uno::Reference< uno::XInterface > & xDoc )
{
rtl::OUString aId;
// Try to get the UID directly from the document.
uno::Reference< beans::XPropertySet > xPropSet( xDoc, uno::UNO_QUERY );
if ( xPropSet.is() )
{
try
{
uno::Any aValue = xPropSet->getPropertyValue(
rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM( "RuntimeUID" ) ) );
aValue >>= aId;
}
catch ( beans::UnknownPropertyException const & )
{
// Not actually an error. Property is optional.
}
catch ( lang::WrappedTargetException const & )
{
OSL_ENSURE( false, "Caught WrappedTargetException!" );
}
}
if ( aId.getLength() == 0 )
{
// fallback: generate UID from document's this pointer.
// normalize the interface pointer first. Else, calls with different
// interfaces to the same object (say, XFoo and XBar) will produce
// different IDs
uno::Reference< uno::XInterface > xNormalizedIFace( xDoc, uno::UNO_QUERY );
sal_Int64 nId = reinterpret_cast< sal_Int64 >( xNormalizedIFace.get() );
aId = rtl::OUString::valueOf( nId );
}
OSL_ENSURE( aId.getLength() > 0, "getDocumentId - Empty id!" );
return aId;
}
//=========================================================================
//
// document::XEventListener
//
//=========================================================================
// virtual
void SAL_CALL OfficeDocumentsManager::notifyEvent(
const document::EventObject & Event )
throw ( uno::RuntimeException )
{
/*
Events documentation: OOo Developer's Guide / Writing UNO Components / Jobs
*/
if ( Event.EventName.equalsAsciiL(
RTL_CONSTASCII_STRINGPARAM( "OnLoadFinished" ) ) // document loaded
|| Event.EventName.equalsAsciiL(
RTL_CONSTASCII_STRINGPARAM( "OnCreate" ) ) ) // document created
{
if ( isOfficeDocument( Event.Source ) )
{
osl::MutexGuard aGuard( m_aMtx );
uno::Reference< frame::XModel >
xModel( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
DocumentList::const_iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
if ( (*it).second.xModel == xModel )
{
// already known.
break;
}
++it;
}
if ( it == m_aDocs.end() )
{
// new document
uno::Reference< document::XStorageBasedDocument >
xDoc( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xDoc.is(), "Got no document::XStorageBasedDocument!" );
uno::Reference< embed::XStorage > xStorage
= xDoc->getDocumentStorage();
OSL_ENSURE( xStorage.is(), "Got no document storage!" );
rtl:: OUString aDocId = getDocumentId( Event.Source );
rtl:: OUString aTitle = DocumentInfo::getDocumentTitle(
uno::Reference< frame::XModel >( Event.Source, uno::UNO_QUERY ) );
m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xCloseBroadcaster.is(),
"OnLoadFinished/OnCreate event: got no close broadcaster!" );
if ( xCloseBroadcaster.is() )
xCloseBroadcaster->addCloseListener( m_xDocCloseListener );
// Propagate document closure.
OSL_ENSURE( m_pDocEventListener,
"OnLoadFinished/OnCreate event: no owner for insert event propagation!" );
if ( m_pDocEventListener )
m_pDocEventListener->notifyDocumentOpened( aDocId );
}
}
}
else if ( Event.EventName.equalsAsciiL(
RTL_CONSTASCII_STRINGPARAM( "OfficeDocumentsListener::notifyClosing" ) ) )
{
if ( isOfficeDocument( Event.Source ) )
{
// Document has been closed (unloaded)
// #163732# - Official event "OnUnload" does not work here. Event
// gets fired to early. Other OnUnload listeners called after this
// listener may still need TDOC access to the document. Remove the
// document from TDOC docs list on XCloseListener::notifyClosing.
// See OfficeDocumentsManager::OfficeDocumentsListener::notifyClosing.
osl::MutexGuard aGuard( m_aMtx );
uno::Reference< frame::XModel >
xModel( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
DocumentList::iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
if ( (*it).second.xModel == xModel )
{
// Propagate document closure.
OSL_ENSURE( m_pDocEventListener,
"OnUnload event: no owner for close event propagation!" );
if ( m_pDocEventListener )
{
rtl::OUString aDocId( (*it).first );
m_pDocEventListener->notifyDocumentClosed( aDocId );
}
break;
}
++it;
}
OSL_ENSURE( it != m_aDocs.end(),
"OnUnload event notified for unknown document!" );
if ( it != m_aDocs.end() )
{
uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xCloseBroadcaster.is(),
"OnUnload event: got no XCloseBroadcaster from XModel" );
if ( xCloseBroadcaster.is() )
xCloseBroadcaster->removeCloseListener( m_xDocCloseListener );
m_aDocs.erase( it );
}
}
}
else if ( Event.EventName.equalsAsciiL(
RTL_CONSTASCII_STRINGPARAM( "OnSaveDone" ) ) )
{
if ( isOfficeDocument( Event.Source ) )
{
osl::MutexGuard aGuard( m_aMtx );
uno::Reference< frame::XModel >
xModel( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
DocumentList::iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
if ( (*it).second.xModel == xModel )
{
// Storage gets exchanged while saving.
uno::Reference< document::XStorageBasedDocument >
xDoc( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xDoc.is(),
"Got no document::XStorageBasedDocument!" );
uno::Reference< embed::XStorage > xStorage
= xDoc->getDocumentStorage();
OSL_ENSURE( xStorage.is(), "Got no document storage!" );
(*it).second.xStorage = xStorage;
break;
}
++it;
}
OSL_ENSURE( it != m_aDocs.end(),
"OnSaveDone event notified for unknown document!" );
}
}
else if ( Event.EventName.equalsAsciiL(
RTL_CONSTASCII_STRINGPARAM( "OnSaveAsDone" ) ) )
{
if ( isOfficeDocument( Event.Source ) )
{
osl::MutexGuard aGuard( m_aMtx );
uno::Reference< frame::XModel >
xModel( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
DocumentList::iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
if ( (*it).second.xModel == xModel )
{
// Storage gets exchanged while saving.
uno::Reference< document::XStorageBasedDocument >
xDoc( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xDoc.is(),
"Got no document::XStorageBasedDocument!" );
uno::Reference< embed::XStorage > xStorage
= xDoc->getDocumentStorage();
OSL_ENSURE( xStorage.is(), "Got no document storage!" );
(*it).second.xStorage = xStorage;
// Adjust title.
(*it).second.aTitle = DocumentInfo::getDocumentTitle( xModel );
break;
}
++it;
}
OSL_ENSURE( it != m_aDocs.end(),
"OnSaveAsDone event notified for unknown document!" );
}
}
else if ( Event.EventName.equalsAsciiL(
RTL_CONSTASCII_STRINGPARAM( "OnTitleChanged" ) ) )
{
if ( isOfficeDocument( Event.Source ) )
{
osl::MutexGuard aGuard( m_aMtx );
uno::Reference< frame::XModel >
xModel( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xModel.is(), "Got no frame::XModel!" );
DocumentList::iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
if ( (*it).second.xModel == xModel )
{
// Adjust title.
rtl:: OUString aTitle = DocumentInfo::getDocumentTitle( xModel );
(*it).second.aTitle = aTitle;
// Adjust storage.
uno::Reference< document::XStorageBasedDocument >
xDoc( Event.Source, uno::UNO_QUERY );
OSL_ENSURE( xDoc.is(), "Got no document::XStorageBasedDocument!" );
uno::Reference< embed::XStorage > xStorage
= xDoc->getDocumentStorage();
OSL_ENSURE( xDoc.is(), "Got no document storage!" );
rtl:: OUString aDocId = getDocumentId( Event.Source );
m_aDocs[ aDocId ] = StorageInfo( aTitle, xStorage, xModel );
break;
}
++it;
}
// OSL_ENSURE( it != m_aDocs.end(),
// "TitleChanged event notified for unknown document!" );
// TODO: re-enable this assertion. It has been disabled for now, since it breaks the assertion-free smoketest,
// and the fix is more difficult than what can be done now.
// The problem is that at the moment, when you close a SFX-based document via API, it will first
// fire the notifyClosing event, which will make the OfficeDocumentsManager remove the doc from its list.
// Then, it will notify an OnTitleChanged, then an OnUnload. Documents closed via call the notifyClosing
// *after* OnUnload and all other On* events.
// In agreement with MBA, the implementation for SfxBaseModel::Close should be changed to also send notifyClosing
// as last event. When this happens, the assertion here must be enabled, again.
// There is no bug for this, yet - IZ is currently down due to the Kenai migration.
// 2011-02-23 / frank.schoenheit@sun.com
}
}
}
//=========================================================================
//
// lang::XEventListener (base of document::XEventListener)
//
//=========================================================================
// virtual
void SAL_CALL OfficeDocumentsManager::disposing(
const lang::EventObject& /*Source*/ )
throw ( uno::RuntimeException )
{
}
//=========================================================================
//
// Non-interface.
//
//=========================================================================
// static
uno::Reference< document::XEventBroadcaster >
OfficeDocumentsManager::createDocumentEventNotifier(
const uno::Reference< lang::XMultiServiceFactory >& rXSMgr )
{
uno::Reference< uno::XInterface > xIfc;
try
{
xIfc = rXSMgr->createInstance(
rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"com.sun.star.frame.GlobalEventBroadcaster" ) ) );
}
catch ( uno::Exception const & )
{
// handled below.
}
OSL_ENSURE(
xIfc.is(),
"Could not instanciate com.sun.star.frame.GlobalEventBroadcaster" );
if ( xIfc.is() )
{
uno::Reference< document::XEventBroadcaster > xBC(
xIfc, uno::UNO_QUERY );
OSL_ENSURE(
xBC.is(),
"com.sun.star.frame.GlobalEventBroadcaster does not implement "
"interface com.sun.star.document.XEventBroadcaster!" );
return xBC;
}
else
return uno::Reference< document::XEventBroadcaster >();
}
//=========================================================================
void OfficeDocumentsManager::buildDocumentsList()
{
OSL_ENSURE( m_xDocEvtNotifier.is(),
"OfficeDocumentsManager::buildDocumentsList - "
"No document event notifier!" );
uno::Reference< container::XEnumerationAccess > xEnumAccess(
m_xDocEvtNotifier, uno::UNO_QUERY_THROW );
uno::Reference< container::XEnumeration > xEnum
= xEnumAccess->createEnumeration();
osl::MutexGuard aGuard( m_aMtx );
while ( xEnum->hasMoreElements() )
{
uno::Any aValue = xEnum->nextElement();
// container::NoSuchElementException
// lang::WrappedTargetException
try
{
uno::Reference< frame::XModel > xModel;
aValue >>= xModel;
if ( xModel.is() )
{
if ( isOfficeDocument( xModel ) )
{
DocumentList::const_iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
if ( (*it).second.xModel == xModel )
{
// already known.
break;
}
++it;
}
if ( it == m_aDocs.end() )
{
// new document
rtl::OUString aDocId = getDocumentId( xModel );
rtl::OUString aTitle = DocumentInfo::getDocumentTitle( xModel );
uno::Reference< document::XStorageBasedDocument >
xDoc( xModel, uno::UNO_QUERY );
OSL_ENSURE( xDoc.is(),
"Got no document::XStorageBasedDocument!" );
uno::Reference< embed::XStorage > xStorage
= xDoc->getDocumentStorage();
OSL_ENSURE( xDoc.is(), "Got no document storage!" );
m_aDocs[ aDocId ]
= StorageInfo( aTitle, xStorage, xModel );
uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster(
xModel, uno::UNO_QUERY );
OSL_ENSURE( xCloseBroadcaster.is(),
"buildDocumentsList: got no close broadcaster!" );
if ( xCloseBroadcaster.is() )
xCloseBroadcaster->addCloseListener( m_xDocCloseListener );
}
}
}
}
catch ( lang::DisposedException const & )
{
// Note: Due to race conditions the XEnumeration can
// contains docs that already have been closed
}
}
}
//=========================================================================
uno::Reference< embed::XStorage >
OfficeDocumentsManager::queryStorage( const rtl::OUString & rDocId )
{
osl::MutexGuard aGuard( m_aMtx );
DocumentList::const_iterator it = m_aDocs.find( rDocId );
if ( it == m_aDocs.end() )
return uno::Reference< embed::XStorage >();
return (*it).second.xStorage;
}
//=========================================================================
rtl::OUString OfficeDocumentsManager::queryDocumentId(
const uno::Reference< frame::XModel > & xModel )
{
return getDocumentId( xModel );
}
//=========================================================================
uno::Reference< frame::XModel >
OfficeDocumentsManager::queryDocumentModel( const rtl::OUString & rDocId )
{
osl::MutexGuard aGuard( m_aMtx );
DocumentList::const_iterator it = m_aDocs.find( rDocId );
if ( it == m_aDocs.end() )
return uno::Reference< frame::XModel >();
return (*it).second.xModel;
}
//=========================================================================
uno::Sequence< rtl::OUString > OfficeDocumentsManager::queryDocuments()
{
osl::MutexGuard aGuard( m_aMtx );
uno::Sequence< rtl::OUString > aRet( m_aDocs.size() );
sal_Int32 nPos = 0;
DocumentList::const_iterator it = m_aDocs.begin();
while ( it != m_aDocs.end() )
{
aRet[ nPos ] = (*it).first;
++it;
++nPos;
}
return aRet;
}
//=========================================================================
rtl::OUString
OfficeDocumentsManager::queryStorageTitle( const rtl::OUString & rDocId )
{
osl::MutexGuard aGuard( m_aMtx );
DocumentList::const_iterator it = m_aDocs.find( rDocId );
if ( it == m_aDocs.end() )
return rtl::OUString();
return (*it).second.aTitle;
}
//=========================================================================
bool OfficeDocumentsManager::isDocumentPreview(
const uno::Reference< frame::XModel > & xModel )
{
if ( !xModel.is() )
return false;
::comphelper::NamedValueCollection aArgs(
xModel->getArgs() );
sal_Bool bIsPreview = aArgs.getOrDefault( "Preview", sal_False );
return bIsPreview;
}
//=========================================================================
bool OfficeDocumentsManager::isHelpDocument(
const uno::Reference< frame::XModel > & xModel )
{
if ( !xModel.is() )
return false;
::rtl::OUString sURL( xModel->getURL() );
if ( sURL.matchAsciiL( RTL_CONSTASCII_STRINGPARAM( "vnd.sun.star.help://" ) ) )
return true;
return false;
}
//=========================================================================
bool OfficeDocumentsManager::isWithoutOrInTopLevelFrame(
const uno::Reference< frame::XModel > & xModel )
{
if ( !xModel.is() )
return false;
uno::Reference< frame::XController > xController
= xModel->getCurrentController();
if ( xController.is() )
{
uno::Reference< frame::XFrame > xFrame
= xController->getFrame();
if ( xFrame.is() )
{
// don't use XFrame::isTop here. This nowadays excludes
// "sub documents" such as forms embedded in database documents
uno::Reference< awt::XTopWindow > xFrameContainer(
xFrame->getContainerWindow(), uno::UNO_QUERY );
if ( !xFrameContainer.is() )
return false;
}
}
return true;
}
//=========================================================================
bool OfficeDocumentsManager::isBasicIDE(
const uno::Reference< frame::XModel > & xModel )
{
if ( !m_xModuleMgr.is() )
{
osl::MutexGuard aGuard( m_aMtx );
if ( !m_xModuleMgr.is() )
{
try
{
m_xModuleMgr
= uno::Reference<
frame::XModuleManager >(
m_xSMgr->createInstance(
rtl::OUString(
RTL_CONSTASCII_USTRINGPARAM(
"com.sun.star.frame.ModuleManager" ) ) ),
uno::UNO_QUERY );
}
catch ( uno::Exception const & )
{
// handled below.
}
OSL_ENSURE( m_xModuleMgr .is(),
"Could not instanciate ModuleManager service!" );
}
}
if ( m_xModuleMgr.is() )
{
rtl::OUString aModule;
try
{
aModule = m_xModuleMgr->identify( xModel );
}
catch ( lang::IllegalArgumentException const & )
{
OSL_ENSURE( false, "Caught IllegalArgumentException!" );
}
catch ( frame::UnknownModuleException const & )
{
OSL_ENSURE( false, "Caught UnknownModuleException!" );
}
if ( aModule.getLength() > 0 )
{
// Filter unwanted items, that are no real documents.
if ( aModule.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM(
"com.sun.star.script.BasicIDE" ) ) )
{
return true;
}
}
}
return false;
}
//=========================================================================
bool OfficeDocumentsManager::isOfficeDocument(
const uno::Reference< uno::XInterface > & xDoc )
{
uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY );
uno::Reference< document::XStorageBasedDocument >
xStorageBasedDoc( xModel, uno::UNO_QUERY );
if ( !xStorageBasedDoc.is() )
return false;
if ( !isWithoutOrInTopLevelFrame( xModel ) )
return false;
if ( isDocumentPreview( xModel ) )
return false;
if ( isHelpDocument( xModel ) )
return false;
if ( isBasicIDE( xModel ) )
return false;
return true;
}