1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dispatchwatcher.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_desktop.hxx"
34 #include "dispatchwatcher.hxx"
35 #include <rtl/ustring.hxx>
36 #include <tools/string.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/synchronousdispatch.hxx>
39 #include <com/sun/star/util/XCloseable.hpp>
40 #include <com/sun/star/util/CloseVetoException.hpp>
41 #include <com/sun/star/task/XInteractionHandler.hpp>
42 #include <com/sun/star/util/URL.hpp>
43 #include <com/sun/star/frame/XDesktop.hpp>
44 #include <com/sun/star/container/XEnumeration.hpp>
45 #include <com/sun/star/frame/XFramesSupplier.hpp>
46 #include <com/sun/star/frame/XDispatch.hpp>
47 #include <com/sun/star/frame/XComponentLoader.hpp>
48 #include <com/sun/star/beans/PropertyValue.hpp>
49 #include <com/sun/star/view/XPrintable.hpp>
50 #include <com/sun/star/frame/XDispatchProvider.hpp>
51 #include <com/sun/star/util/XURLTransformer.hpp>
52 #include <com/sun/star/document/MacroExecMode.hpp>
53 #include <com/sun/star/document/UpdateDocMode.hpp>
55 #include <tools/urlobj.hxx>
56 #include <comphelper/mediadescriptor.hxx>
60 using namespace ::rtl
;
61 using namespace ::osl
;
62 using namespace ::com::sun::star::uno
;
63 using namespace ::com::sun::star::util
;
64 using namespace ::com::sun::star::lang
;
65 using namespace ::com::sun::star::frame
;
66 using namespace ::com::sun::star::container
;
67 using namespace ::com::sun::star::beans
;
68 using namespace ::com::sun::star::view
;
74 const String
& rName
, boost::optional
< rtl::OUString
> const & cwdUrl
);
78 DispatchHolder( const URL
& rURL
, Reference
< XDispatch
>& rDispatch
) :
79 aURL( rURL
), xDispatch( rDispatch
) {}
83 Reference
< XDispatch
> xDispatch
;
86 Mutex
* DispatchWatcher::pWatcherMutex
= NULL
;
88 Mutex
& DispatchWatcher::GetMutex()
92 ::osl::MutexGuard
aGuard( ::osl::Mutex::getGlobalMutex() );
94 pWatcherMutex
= new osl::Mutex();
97 return *pWatcherMutex
;
100 // Create or get the dispatch watcher implementation. This implementation must be
101 // a singleton to prevent access to the framework after it wants to terminate.
102 DispatchWatcher
* DispatchWatcher::GetDispatchWatcher()
104 static Reference
< XInterface
> xDispatchWatcher
;
105 static DispatchWatcher
* pDispatchWatcher
= NULL
;
107 if ( !xDispatchWatcher
.is() )
109 ::osl::MutexGuard
aGuard( GetMutex() );
111 if ( !xDispatchWatcher
.is() )
113 pDispatchWatcher
= new DispatchWatcher();
115 // We have to hold a reference to ourself forever to prevent our own destruction.
116 xDispatchWatcher
= static_cast< cppu::OWeakObject
*>( pDispatchWatcher
);
120 return pDispatchWatcher
;
124 DispatchWatcher::DispatchWatcher()
130 DispatchWatcher::~DispatchWatcher()
135 sal_Bool
DispatchWatcher::executeDispatchRequests( const DispatchList
& aDispatchRequestsList
, bool bNoTerminate
)
137 Reference
< XComponentLoader
> xDesktop( ::comphelper::getProcessServiceFactory()->createInstance(
138 OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ),
141 DispatchList::const_iterator p
;
142 std::vector
< DispatchHolder
> aDispatches
;
143 ::rtl::OUString
aAsTemplateArg( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate"));
145 for ( p
= aDispatchRequestsList
.begin(); p
!= aDispatchRequestsList
.end(); p
++ )
148 const DispatchRequest
& aDispatchRequest
= *p
;
150 // create parameter array
151 sal_Int32 nCount
= 4;
152 if ( aDispatchRequest
.aPreselectedFactory
.getLength() )
155 // we need more properties for a print/print to request
156 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
157 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
)
160 Sequence
< PropertyValue
> aArgs( nCount
);
162 // mark request as user interaction from outside
163 aArgs
[0].Name
= ::rtl::OUString::createFromAscii("Referer");
164 aArgs
[0].Value
<<= ::rtl::OUString::createFromAscii("private:OpenEvent");
166 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
167 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
)
169 aArgs
[1].Name
= ::rtl::OUString::createFromAscii("ReadOnly");
170 aArgs
[2].Name
= ::rtl::OUString::createFromAscii("OpenNewView");
171 aArgs
[3].Name
= ::rtl::OUString::createFromAscii("Hidden");
172 aArgs
[4].Name
= ::rtl::OUString::createFromAscii("Silent");
176 Reference
< com::sun::star::task::XInteractionHandler
> xInteraction(
177 ::comphelper::getProcessServiceFactory()->createInstance( OUString::createFromAscii("com.sun.star.task.InteractionHandler") ),
178 com::sun::star::uno::UNO_QUERY
);
180 aArgs
[1].Name
= OUString::createFromAscii( "InteractionHandler" );
181 aArgs
[1].Value
<<= xInteraction
;
183 sal_Int16 nMacroExecMode
= ::com::sun::star::document::MacroExecMode::USE_CONFIG
;
184 aArgs
[2].Name
= OUString::createFromAscii( "MacroExecutionMode" );
185 aArgs
[2].Value
<<= nMacroExecMode
;
187 sal_Int16 nUpdateDoc
= ::com::sun::star::document::UpdateDocMode::ACCORDING_TO_CONFIG
;
188 aArgs
[3].Name
= OUString::createFromAscii( "UpdateDocMode" );
189 aArgs
[3].Value
<<= nUpdateDoc
;
192 if ( aDispatchRequest
.aPreselectedFactory
.getLength() )
194 aArgs
[nCount
-1].Name
= ::comphelper::MediaDescriptor::PROP_DOCUMENTSERVICE();
195 aArgs
[nCount
-1].Value
<<= aDispatchRequest
.aPreselectedFactory
;
198 String
aName( GetURL_Impl( aDispatchRequest
.aURL
, aDispatchRequest
.aCwdUrl
) );
199 ::rtl::OUString
aTarget( RTL_CONSTASCII_USTRINGPARAM("_default") );
201 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
202 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
)
204 // documents opened for printing are opened readonly because they must be opened as a new document and this
205 // document could be open already
206 aArgs
[1].Value
<<= sal_True
;
208 // always open a new document for printing, because it must be disposed afterwards
209 aArgs
[2].Value
<<= sal_True
;
211 // printing is done in a hidden view
212 aArgs
[3].Value
<<= sal_True
;
214 // load document for printing without user interaction
215 aArgs
[4].Value
<<= sal_True
;
217 // hidden documents should never be put into open tasks
218 aTarget
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_blank") );
221 // load the document ... if they are loadable!
222 // Otherwise try to dispatch it ...
223 Reference
< XPrintable
> xDoc
;
225 ( aName
.CompareToAscii( ".uno" , 4 ) == COMPARE_EQUAL
) ||
226 ( aName
.CompareToAscii( "slot:" , 5 ) == COMPARE_EQUAL
) ||
227 ( aName
.CompareToAscii( "macro:", 6 ) == COMPARE_EQUAL
) ||
228 ( aName
.CompareToAscii("vnd.sun.star.script", 19) == COMPARE_EQUAL
)
231 // Attention: URL must be parsed full. Otherwise some detections on it will fail!
232 // It doesnt matter, if parser isn't available. Because; We try loading of URL then ...
234 aURL
.Complete
= aName
;
236 Reference
< XDispatch
> xDispatcher
;
237 Reference
< XDispatchProvider
> xProvider ( xDesktop
, UNO_QUERY
);
238 Reference
< XURLTransformer
> xParser ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY
);
240 if( xParser
.is() == sal_True
)
241 xParser
->parseStrict( aURL
);
243 if( xProvider
.is() == sal_True
)
244 xDispatcher
= xProvider
->queryDispatch( aURL
, ::rtl::OUString(), 0 );
246 if( xDispatcher
.is() == sal_True
)
249 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
250 // Remember request so we can find it in statusChanged!
251 m_aRequestContainer
.insert( DispatchWatcherHashMap::value_type( aURL
.Complete
, (sal_Int32
)1 ) );
255 // Use local vector to store dispatcher because we have to fill our request container before
256 // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!!
257 aDispatches
.push_back( DispatchHolder( aURL
, xDispatcher
));
260 else if ( ( aName
.CompareToAscii( "service:" , 8 ) == COMPARE_EQUAL
) )
262 // TODO: the dispatch has to be done for loadComponentFromURL as well. Please ask AS for more details.
264 aURL
.Complete
= aName
;
266 Reference
< XDispatch
> xDispatcher
;
267 Reference
< XDispatchProvider
> xProvider ( xDesktop
, UNO_QUERY
);
268 Reference
< XURLTransformer
> xParser ( ::comphelper::getProcessServiceFactory()->createInstance( OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.util.URLTransformer")) ), ::com::sun::star::uno::UNO_QUERY
);
270 if( xParser
.is() == sal_True
)
271 xParser
->parseStrict( aURL
);
273 if( xProvider
.is() == sal_True
)
274 xDispatcher
= xProvider
->queryDispatch( aURL
, ::rtl::OUString(), 0 );
276 if( xDispatcher
.is() == sal_True
)
280 // We have to be listener to catch errors during dispatching URLs.
281 // Otherwise it would be possible to have an office running without an open
283 Sequence
< PropertyValue
> aArgs2(1);
284 aArgs2
[0].Name
= ::rtl::OUString::createFromAscii("SynchronMode");
285 aArgs2
[0].Value
<<= sal_True
;
286 Reference
< XNotifyingDispatch
> xDisp( xDispatcher
, UNO_QUERY
);
288 xDisp
->dispatchWithNotification( aURL
, aArgs2
, DispatchWatcher::GetDispatchWatcher() );
290 xDispatcher
->dispatch( aURL
, aArgs2
);
292 catch ( ::com::sun::star::uno::Exception
& )
294 OUString aMsg
= OUString::createFromAscii(
295 "Desktop::OpenDefault() IllegalArgumentException while calling XNotifyingDispatch: ");
296 OSL_ENSURE( sal_False
, OUStringToOString(aMsg
, RTL_TEXTENCODING_ASCII_US
).getStr());
302 INetURLObject
aObj( aName
);
303 if ( aObj
.GetProtocol() == INET_PROT_PRIVATE
)
304 aTarget
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("_default") );
306 // Set "AsTemplate" argument according to request type
307 if ( aDispatchRequest
.aRequestType
== REQUEST_FORCENEW
||
308 aDispatchRequest
.aRequestType
== REQUEST_FORCEOPEN
)
310 sal_Int32 nIndex
= aArgs
.getLength();
311 aArgs
.realloc( nIndex
+1 );
312 aArgs
[nIndex
].Name
= aAsTemplateArg
;
313 if ( aDispatchRequest
.aRequestType
== REQUEST_FORCENEW
)
314 aArgs
[nIndex
].Value
<<= sal_True
;
316 aArgs
[nIndex
].Value
<<= sal_False
;
319 // if we are called in viewmode, open document read-only
321 if(aDispatchRequest
.aRequestType
== REQUEST_VIEW
) {
322 sal_Int32 nIndex
= aArgs
.getLength();
323 aArgs
.realloc(nIndex
+1);
324 aArgs
[nIndex
].Name
= OUString::createFromAscii("ReadOnly");
325 aArgs
[nIndex
].Value
<<= sal_True
;
328 // if we are called with -start set Start in mediadescriptor
329 if(aDispatchRequest
.aRequestType
== REQUEST_START
) {
330 sal_Int32 nIndex
= aArgs
.getLength();
331 aArgs
.realloc(nIndex
+1);
332 aArgs
[nIndex
].Name
= OUString::createFromAscii("StartPresentation");
333 aArgs
[nIndex
].Value
<<= sal_True
;
336 // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism.
340 xDoc
= Reference
< XPrintable
>( ::comphelper::SynchronousDispatch::dispatch( xDesktop
, aName
, aTarget
, 0, aArgs
), UNO_QUERY
);
341 //xDoc = Reference < XPrintable >( xDesktop->loadComponentFromURL( aName, aTarget, 0, aArgs ), UNO_QUERY );
343 catch ( ::com::sun::star::lang::IllegalArgumentException
& iae
)
345 OUString aMsg
= OUString::createFromAscii(
346 "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL: ")
348 OSL_ENSURE( sal_False
, OUStringToOString(aMsg
, RTL_TEXTENCODING_ASCII_US
).getStr());
350 catch (com::sun::star::io::IOException
& ioe
)
352 OUString aMsg
= OUString::createFromAscii(
353 "Dispatchwatcher IOException while calling loadComponentFromURL: ")
355 OSL_ENSURE( sal_False
, OUStringToOString(aMsg
, RTL_TEXTENCODING_ASCII_US
).getStr());
357 if ( aDispatchRequest
.aRequestType
== REQUEST_OPEN
||
358 aDispatchRequest
.aRequestType
== REQUEST_VIEW
||
359 aDispatchRequest
.aRequestType
== REQUEST_START
||
360 aDispatchRequest
.aRequestType
== REQUEST_FORCEOPEN
||
361 aDispatchRequest
.aRequestType
== REQUEST_FORCENEW
)
363 // request is completed
364 OfficeIPCThread::RequestsCompleted( 1 );
366 else if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
367 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
)
371 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
)
373 // create the printer
374 Sequence
< PropertyValue
> aPrinterArgs( 1 );
375 aPrinterArgs
[0].Name
= ::rtl::OUString::createFromAscii("Name");
376 aPrinterArgs
[0].Value
<<= ::rtl::OUString( aDispatchRequest
.aPrinterName
);
377 xDoc
->setPrinter( aPrinterArgs
);
380 // print ( also without user interaction )
381 Sequence
< PropertyValue
> aPrinterArgs( 1 );
382 aPrinterArgs
[0].Name
= ::rtl::OUString::createFromAscii("Wait");
383 aPrinterArgs
[0].Value
<<= ( sal_Bool
) sal_True
;
384 xDoc
->print( aPrinterArgs
);
388 // place error message here ...
391 // remove the document
394 Reference
< XCloseable
> xClose( xDoc
, UNO_QUERY
);
396 xClose
->close( sal_True
);
399 Reference
< XComponent
> xComp( xDoc
, UNO_QUERY
);
404 catch ( com::sun::star::util::CloseVetoException
& )
408 // request is completed
409 OfficeIPCThread::RequestsCompleted( 1 );
414 if ( aDispatches
.size() > 0 )
416 // Execute all asynchronous dispatches now after we placed them into our request container!
417 Sequence
< PropertyValue
> aArgs( 2 );
418 aArgs
[0].Name
= ::rtl::OUString::createFromAscii("Referer");
419 aArgs
[0].Value
<<= ::rtl::OUString::createFromAscii("private:OpenEvent");
420 aArgs
[1].Name
= ::rtl::OUString::createFromAscii("SynchronMode");
421 aArgs
[1].Value
<<= sal_True
;
423 for ( sal_uInt32 n
= 0; n
< aDispatches
.size(); n
++ )
425 Reference
< XDispatch
> xDispatch
= aDispatches
[n
].xDispatch
;
426 Reference
< XNotifyingDispatch
> xDisp( xDispatch
, UNO_QUERY
);
428 xDisp
->dispatchWithNotification( aDispatches
[n
].aURL
, aArgs
, this );
431 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
434 xDispatch
->dispatch( aDispatches
[n
].aURL
, aArgs
);
439 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
440 bool bEmpty
= (m_nRequestCount
== 0);
443 // No more asynchronous requests?
444 // The requests are removed from the request container after they called back to this
445 // implementation via statusChanged!!
446 if ( bEmpty
&& !bNoTerminate
/*m_aRequestContainer.empty()*/ )
448 // We have to check if we have an open task otherwise we have to shutdown the office.
449 Reference
< XFramesSupplier
> xTasksSupplier( xDesktop
, UNO_QUERY
);
452 Reference
< XElementAccess
> xList( xTasksSupplier
->getFrames(), UNO_QUERY
);
454 if ( !xList
->hasElements() )
456 // We don't have any task open so we have to shutdown ourself!!
457 Reference
< XDesktop
> xDesktop2( xTasksSupplier
, UNO_QUERY
);
458 if ( xDesktop2
.is() )
459 return xDesktop2
->terminate();
467 void SAL_CALL
DispatchWatcher::disposing( const ::com::sun::star::lang::EventObject
& )
468 throw(::com::sun::star::uno::RuntimeException
)
473 void SAL_CALL
DispatchWatcher::dispatchFinished( const DispatchResultEvent
& ) throw( RuntimeException
)
475 osl::ClearableMutexGuard
aGuard( GetMutex() );
476 sal_Int16 nCount
= --m_nRequestCount
;
478 OfficeIPCThread::RequestsCompleted( 1 );
480 // Find request in our hash map and remove it as a pending request
481 DispatchWatcherHashMap::iterator pDispatchEntry = m_aRequestContainer.find( rEvent.FeatureURL.Complete ) ;
482 if ( pDispatchEntry != m_aRequestContainer.end() )
484 m_aRequestContainer.erase( pDispatchEntry );
486 OfficeIPCThread::RequestsCompleted( 1 );
491 if ( !nCount
&& !OfficeIPCThread::AreRequestsPending() )
493 // We have to check if we have an open task otherwise we have to shutdown the office.
494 Reference
< XFramesSupplier
> xTasksSupplier( ::comphelper::getProcessServiceFactory()->createInstance(
495 OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ),
497 Reference
< XElementAccess
> xList( xTasksSupplier
->getFrames(), UNO_QUERY
);
499 if ( !xList
->hasElements() )
501 // We don't have any task open so we have to shutdown ourself!!
502 Reference
< XDesktop
> xDesktop( xTasksSupplier
, UNO_QUERY
);
504 xDesktop
->terminate();