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 #include <sfx2/docfile.hxx>
22 #include <sfx2/docfilt.hxx>
23 #include <sfx2/fcontnr.hxx>
24 #include "osl/file.hxx"
25 #include "sfx2/app.hxx"
26 #include <svl/fstathelper.hxx>
28 #include "dispatchwatcher.hxx"
29 #include <rtl/ustring.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/synchronousdispatch.hxx>
32 #include <com/sun/star/util/XCloseable.hpp>
33 #include <com/sun/star/util/CloseVetoException.hpp>
34 #include <com/sun/star/task/InteractionHandler.hpp>
35 #include <com/sun/star/util/URL.hpp>
36 #include <com/sun/star/frame/Desktop.hpp>
37 #include <com/sun/star/container/XEnumeration.hpp>
38 #include <com/sun/star/frame/XFramesSupplier.hpp>
39 #include <com/sun/star/frame/XDispatch.hpp>
40 #include <com/sun/star/frame/XComponentLoader.hpp>
41 #include <com/sun/star/beans/PropertyValue.hpp>
42 #include <com/sun/star/view/XPrintable.hpp>
43 #include <com/sun/star/frame/XDispatchProvider.hpp>
44 #include <com/sun/star/util/URLTransformer.hpp>
45 #include <com/sun/star/util/XURLTransformer.hpp>
46 #include <com/sun/star/document/MacroExecMode.hpp>
47 #include <com/sun/star/document/UpdateDocMode.hpp>
48 #include <com/sun/star/frame/XStorable.hpp>
50 #include <tools/urlobj.hxx>
51 #include <unotools/mediadescriptor.hxx>
54 #include <osl/thread.hxx>
55 #include <rtl/instance.hxx>
57 using namespace ::osl
;
58 using namespace ::com::sun::star::uno
;
59 using namespace ::com::sun::star::util
;
60 using namespace ::com::sun::star::lang
;
61 using namespace ::com::sun::star::frame
;
62 using namespace ::com::sun::star::container
;
63 using namespace ::com::sun::star::beans
;
64 using namespace ::com::sun::star::view
;
65 using namespace ::com::sun::star::task
;
71 const OUString
& rName
, boost::optional
< OUString
> const & cwdUrl
);
75 DispatchHolder( const URL
& rURL
, Reference
< XDispatch
>& rDispatch
) :
76 aURL( rURL
), xDispatch( rDispatch
) {}
80 Reference
< XDispatch
> xDispatch
;
83 static OUString
impl_GetFilterFromExt( OUString aUrl
, SfxFilterFlags nFlags
,
87 SfxMedium
* pMedium
= new SfxMedium( aUrl
,
90 const SfxFilter
*pSfxFilter
= NULL
;
91 if( nFlags
== SFX_FILTER_EXPORT
)
93 SfxFilterMatcher( aAppl
).GuessFilterIgnoringContent( *pMedium
, &pSfxFilter
, nFlags
, 0 );
97 SFX_APP()->GetFilterMatcher().GuessFilter( *pMedium
, &pSfxFilter
, nFlags
, 0 );
102 if (nFlags
== SFX_FILTER_EXPORT
)
103 aFilter
= pSfxFilter
->GetFilterName();
105 aFilter
= pSfxFilter
->GetServiceName();
111 static OUString
impl_GuessFilter( OUString aUrlIn
, OUString aUrlOut
)
113 /* aAppl can also be set to Factory like scalc, swriter... */
115 aAppl
= impl_GetFilterFromExt( aUrlIn
, SFX_FILTER_IMPORT
, aAppl
);
116 return impl_GetFilterFromExt( aUrlOut
, SFX_FILTER_EXPORT
, aAppl
);
121 class theWatcherMutex
: public rtl::Static
<Mutex
, theWatcherMutex
> {};
124 Mutex
& DispatchWatcher::GetMutex()
126 return theWatcherMutex::get();
129 // Create or get the dispatch watcher implementation. This implementation must be
130 // a singleton to prevent access to the framework after it wants to terminate.
131 DispatchWatcher
* DispatchWatcher::GetDispatchWatcher()
133 static Reference
< XInterface
> xDispatchWatcher
;
134 static DispatchWatcher
* pDispatchWatcher
= NULL
;
136 if ( !xDispatchWatcher
.is() )
138 ::osl::MutexGuard
aGuard( GetMutex() );
140 if ( !xDispatchWatcher
.is() )
142 pDispatchWatcher
= new DispatchWatcher();
144 // We have to hold a reference to ourself forever to prevent our own destruction.
145 xDispatchWatcher
= static_cast< cppu::OWeakObject
*>( pDispatchWatcher
);
149 return pDispatchWatcher
;
153 DispatchWatcher::DispatchWatcher()
159 DispatchWatcher::~DispatchWatcher()
164 sal_Bool
DispatchWatcher::executeDispatchRequests( const DispatchList
& aDispatchRequestsList
, bool bNoTerminate
)
166 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
168 DispatchList::const_iterator p
;
169 std::vector
< DispatchHolder
> aDispatches
;
170 OUString
aAsTemplateArg( "AsTemplate" );
171 sal_Bool bSetInputFilter
= sal_False
;
172 OUString aForcedInputFilter
;
174 for ( p
= aDispatchRequestsList
.begin(); p
!= aDispatchRequestsList
.end(); ++p
)
176 const DispatchRequest
& aDispatchRequest
= *p
;
178 // create parameter array
179 sal_Int32 nCount
= 4;
180 if ( !aDispatchRequest
.aPreselectedFactory
.isEmpty() )
184 if ( aDispatchRequest
.aRequestType
== REQUEST_INFILTER
)
186 bSetInputFilter
= sal_True
;
187 aForcedInputFilter
= aDispatchRequest
.aURL
;
188 OfficeIPCThread::RequestsCompleted( 1 );
192 // we need more properties for a print/print to request
193 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
194 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
||
195 aDispatchRequest
.aRequestType
== REQUEST_BATCHPRINT
||
196 aDispatchRequest
.aRequestType
== REQUEST_CONVERSION
)
199 Sequence
< PropertyValue
> aArgs( nCount
);
201 // mark request as user interaction from outside
202 aArgs
[0].Name
= "Referer";
203 aArgs
[0].Value
<<= OUString("private:OpenEvent");
205 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
206 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
||
207 aDispatchRequest
.aRequestType
== REQUEST_BATCHPRINT
||
208 aDispatchRequest
.aRequestType
== REQUEST_CONVERSION
)
210 aArgs
[1].Name
= "ReadOnly";
211 aArgs
[2].Name
= "OpenNewView";
212 aArgs
[3].Name
= "Hidden";
213 aArgs
[4].Name
= "Silent";
217 Reference
< XInteractionHandler2
> xInteraction(
218 InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), 0) );
220 aArgs
[1].Name
= "InteractionHandler";
221 aArgs
[1].Value
<<= xInteraction
;
223 sal_Int16 nMacroExecMode
= ::com::sun::star::document::MacroExecMode::USE_CONFIG
;
224 aArgs
[2].Name
= "MacroExecutionMode";
225 aArgs
[2].Value
<<= nMacroExecMode
;
227 sal_Int16 nUpdateDoc
= ::com::sun::star::document::UpdateDocMode::ACCORDING_TO_CONFIG
;
228 aArgs
[3].Name
= "UpdateDocMode";
229 aArgs
[3].Value
<<= nUpdateDoc
;
232 if ( !aDispatchRequest
.aPreselectedFactory
.isEmpty() )
234 aArgs
[nCount
-1].Name
= utl::MediaDescriptor::PROP_DOCUMENTSERVICE();
235 aArgs
[nCount
-1].Value
<<= aDispatchRequest
.aPreselectedFactory
;
238 OUString
aName( GetURL_Impl( aDispatchRequest
.aURL
, aDispatchRequest
.aCwdUrl
) );
239 OUString
aTarget("_default");
241 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
242 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
||
243 aDispatchRequest
.aRequestType
== REQUEST_BATCHPRINT
||
244 aDispatchRequest
.aRequestType
== REQUEST_CONVERSION
)
246 // documents opened for printing are opened readonly because they must be opened as a new document and this
247 // document could be open already
248 aArgs
[1].Value
<<= sal_True
;
250 // always open a new document for printing, because it must be disposed afterwards
251 aArgs
[2].Value
<<= sal_True
;
253 // printing is done in a hidden view
254 aArgs
[3].Value
<<= sal_True
;
256 // load document for printing without user interaction
257 aArgs
[4].Value
<<= sal_True
;
259 // hidden documents should never be put into open tasks
262 // load the document ... if they are loadable!
263 // Otherwise try to dispatch it ...
264 Reference
< XPrintable
> xDoc
;
266 ( aName
.startsWith( ".uno" ) ) ||
267 ( aName
.startsWith( "slot:" ) ) ||
268 ( aName
.startsWith( "macro:" ) ) ||
269 ( aName
.startsWith("vnd.sun.star.script") )
272 // Attention: URL must be parsed full. Otherwise some detections on it will fail!
273 // It doesn't matter, if parser isn't available. Because; We try loading of URL then ...
275 aURL
.Complete
= aName
;
277 Reference
< XDispatch
> xDispatcher
;
278 Reference
< XURLTransformer
> xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
280 if( xParser
.is() == sal_True
)
281 xParser
->parseStrict( aURL
);
283 xDispatcher
= xDesktop
->queryDispatch( aURL
, OUString(), 0 );
285 if( xDispatcher
.is() == sal_True
)
288 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
289 // Remember request so we can find it in statusChanged!
290 m_aRequestContainer
.insert( DispatchWatcherHashMap::value_type( aURL
.Complete
, (sal_Int32
)1 ) );
294 // Use local vector to store dispatcher because we have to fill our request container before
295 // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!!
296 aDispatches
.push_back( DispatchHolder( aURL
, xDispatcher
));
299 else if ( ( aName
.startsWith( "service:" ) ) )
301 // TODO: the dispatch has to be done for loadComponentFromURL as well. Please ask AS for more details.
303 aURL
.Complete
= aName
;
305 Reference
< XDispatch
> xDispatcher
;
306 Reference
< XURLTransformer
> xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
308 if( xParser
.is() == sal_True
)
309 xParser
->parseStrict( aURL
);
311 xDispatcher
= xDesktop
->queryDispatch( aURL
, OUString(), 0 );
313 if( xDispatcher
.is() == sal_True
)
317 // We have to be listener to catch errors during dispatching URLs.
318 // Otherwise it would be possible to have an office running without an open
320 Sequence
< PropertyValue
> aArgs2(1);
321 aArgs2
[0].Name
= "SynchronMode";
322 aArgs2
[0].Value
<<= sal_True
;
323 Reference
< XNotifyingDispatch
> xDisp( xDispatcher
, UNO_QUERY
);
325 xDisp
->dispatchWithNotification( aURL
, aArgs2
, DispatchWatcher::GetDispatchWatcher() );
327 xDispatcher
->dispatch( aURL
, aArgs2
);
329 catch (const ::com::sun::star::uno::Exception
& e
)
333 "Desktop::OpenDefault() ignoring Exception while"
334 " calling XNotifyingDispatch: \"" << e
.Message
341 INetURLObject
aObj( aName
);
342 if ( aObj
.GetProtocol() == INET_PROT_PRIVATE
)
343 aTarget
= "_default";
345 // Set "AsTemplate" argument according to request type
346 if ( aDispatchRequest
.aRequestType
== REQUEST_FORCENEW
||
347 aDispatchRequest
.aRequestType
== REQUEST_FORCEOPEN
)
349 sal_Int32 nIndex
= aArgs
.getLength();
350 aArgs
.realloc( nIndex
+1 );
351 aArgs
[nIndex
].Name
= aAsTemplateArg
;
352 if ( aDispatchRequest
.aRequestType
== REQUEST_FORCENEW
)
353 aArgs
[nIndex
].Value
<<= sal_True
;
355 aArgs
[nIndex
].Value
<<= sal_False
;
358 // if we are called in viewmode, open document read-only
359 if(aDispatchRequest
.aRequestType
== REQUEST_VIEW
) {
360 sal_Int32 nIndex
= aArgs
.getLength();
361 aArgs
.realloc(nIndex
+1);
362 aArgs
[nIndex
].Name
= "ReadOnly";
363 aArgs
[nIndex
].Value
<<= sal_True
;
366 // if we are called with -start set Start in mediadescriptor
367 if(aDispatchRequest
.aRequestType
== REQUEST_START
) {
368 sal_Int32 nIndex
= aArgs
.getLength();
369 aArgs
.realloc(nIndex
+1);
370 aArgs
[nIndex
].Name
= "StartPresentation";
371 aArgs
[nIndex
].Value
<<= sal_True
;
374 // Force input filter, if possible
375 if( bSetInputFilter
)
377 sal_Int32 nIndex
= aArgs
.getLength();
378 aArgs
.realloc(nIndex
+1);
379 aArgs
[nIndex
].Name
= "FilterName";
380 aArgs
[nIndex
].Value
<<= aForcedInputFilter
;
383 // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism.
386 xDoc
= Reference
< XPrintable
>( ::comphelper::SynchronousDispatch::dispatch( xDesktop
, aName
, aTarget
, 0, aArgs
), UNO_QUERY
);
388 catch (const ::com::sun::star::lang::IllegalArgumentException
& iae
)
392 "Dispatchwatcher IllegalArgumentException while calling"
393 " loadComponentFromURL: \"" << iae
.Message
<< "\"");
395 catch (const com::sun::star::io::IOException
& ioe
)
399 "Dispatchwatcher IOException while calling"
400 " loadComponentFromURL: \"" << ioe
.Message
<< "\"");
402 if ( aDispatchRequest
.aRequestType
== REQUEST_OPEN
||
403 aDispatchRequest
.aRequestType
== REQUEST_VIEW
||
404 aDispatchRequest
.aRequestType
== REQUEST_START
||
405 aDispatchRequest
.aRequestType
== REQUEST_FORCEOPEN
||
406 aDispatchRequest
.aRequestType
== REQUEST_FORCENEW
)
408 // request is completed
409 OfficeIPCThread::RequestsCompleted( 1 );
411 else if ( aDispatchRequest
.aRequestType
== REQUEST_PRINT
||
412 aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
||
413 aDispatchRequest
.aRequestType
== REQUEST_BATCHPRINT
||
414 aDispatchRequest
.aRequestType
== REQUEST_CONVERSION
)
418 if ( aDispatchRequest
.aRequestType
== REQUEST_CONVERSION
) {
419 Reference
< XStorable
> xStorable( xDoc
, UNO_QUERY
);
420 if ( xStorable
.is() ) {
421 OUString aParam
= aDispatchRequest
.aPrinterName
;
422 sal_Int32 nPathIndex
= aParam
.lastIndexOf( ';' );
423 sal_Int32 nFilterIndex
= aParam
.indexOf( ':' );
424 if( nPathIndex
< nFilterIndex
)
426 OUString aFilterOut
=aParam
.copy( nPathIndex
+1 );
429 sal_Bool bGuess
= sal_False
;
431 if( nFilterIndex
>= 0 )
433 aFilter
= aParam
.copy( nFilterIndex
+1, nPathIndex
-nFilterIndex
-1 );
434 aFilterExt
= aParam
.copy( 0, nFilterIndex
);
440 aFilterExt
= aParam
.copy( 0, nPathIndex
);
442 INetURLObject
aOutFilename( aObj
);
443 aOutFilename
.SetExtension( aFilterExt
);
444 FileBase::getFileURLFromSystemPath( aFilterOut
, aFilterOut
);
445 OUString aOutFile
= aFilterOut
+
447 aOutFilename
.getName();
451 aFilter
= impl_GuessFilter( aName
, aOutFile
);
454 Sequence
<PropertyValue
> conversionProperties( 2 );
455 conversionProperties
[0].Name
= "Overwrite";
456 conversionProperties
[0].Value
<<= sal_True
;
458 conversionProperties
[1].Name
= "FilterName";
459 conversionProperties
[1].Value
<<= aFilter
;
462 FileBase::getSystemPathFromFileURL( aName
, aTempName
);
463 OString aSource8
= OUStringToOString ( aTempName
, RTL_TEXTENCODING_UTF8
);
464 FileBase::getSystemPathFromFileURL( aOutFile
, aTempName
);
465 OString aTargetURL8
= OUStringToOString(aTempName
, RTL_TEXTENCODING_UTF8
);
466 printf("convert %s -> %s using %s\n", aSource8
.getStr(), aTargetURL8
.getStr(),
467 OUStringToOString( aFilter
, RTL_TEXTENCODING_UTF8
).getStr());
468 if( FStatHelper::IsDocument(aOutFile
) )
469 printf("Overwriting: %s\n",OUStringToOString( aTempName
, RTL_TEXTENCODING_UTF8
).getStr() );
472 xStorable
->storeToURL( aOutFile
, conversionProperties
);
474 catch (const Exception
&)
476 fprintf( stderr
, "Error: Please reverify input parameters...\n" );
479 } else if ( aDispatchRequest
.aRequestType
== REQUEST_BATCHPRINT
) {
480 OUString aParam
= aDispatchRequest
.aPrinterName
;
481 sal_Int32 nPathIndex
= aParam
.lastIndexOf( ';' );
484 OUString aPrinterName
;
485 if( nPathIndex
!= -1 )
486 aFilterOut
=aParam
.copy( nPathIndex
+1 );
487 if( nPathIndex
!= 0 )
488 aPrinterName
=aParam
.copy( 0, nPathIndex
);
490 INetURLObject
aOutFilename( aObj
);
491 aOutFilename
.SetExtension( "ps" );
492 FileBase::getFileURLFromSystemPath( aFilterOut
, aFilterOut
);
493 OUString aOutFile
= aFilterOut
+
495 aOutFilename
.getName();
498 FileBase::getSystemPathFromFileURL( aName
, aTempName
);
499 OString aSource8
= OUStringToOString ( aTempName
, RTL_TEXTENCODING_UTF8
);
500 FileBase::getSystemPathFromFileURL( aOutFile
, aTempName
);
501 OString aTargetURL8
= OUStringToOString(aTempName
, RTL_TEXTENCODING_UTF8
);
502 printf("print %s -> %s using %s\n", aSource8
.getStr(), aTargetURL8
.getStr(),
503 aPrinterName
.isEmpty() ?
504 "<default_printer>" : OUStringToOString( aPrinterName
, RTL_TEXTENCODING_UTF8
).getStr() );
506 // create the custom printer, if given
507 Sequence
< PropertyValue
> aPrinterArgs( 1 );
508 if( !aPrinterName
.isEmpty() )
510 aPrinterArgs
[0].Name
= "Name";
511 aPrinterArgs
[0].Value
<<= aPrinterName
;
512 xDoc
->setPrinter( aPrinterArgs
);
515 // print ( also without user interaction )
516 aPrinterArgs
.realloc(2);
517 aPrinterArgs
[0].Name
= "FileName";
518 aPrinterArgs
[0].Value
<<= aOutFile
;
519 aPrinterArgs
[1].Name
= "Wait";
520 aPrinterArgs
[1].Value
<<= ( sal_Bool
) sal_True
;
521 xDoc
->print( aPrinterArgs
);
523 if ( aDispatchRequest
.aRequestType
== REQUEST_PRINTTO
)
525 // create the printer
526 Sequence
< PropertyValue
> aPrinterArgs( 1 );
527 aPrinterArgs
[0].Name
= "Name";
528 aPrinterArgs
[0].Value
<<= OUString( aDispatchRequest
.aPrinterName
);
529 xDoc
->setPrinter( aPrinterArgs
);
532 // print ( also without user interaction )
533 Sequence
< PropertyValue
> aPrinterArgs( 1 );
534 aPrinterArgs
[0].Name
= "Wait";
535 aPrinterArgs
[0].Value
<<= ( sal_Bool
) sal_True
;
536 xDoc
->print( aPrinterArgs
);
541 // place error message here ...
544 // remove the document
547 Reference
< XCloseable
> xClose( xDoc
, UNO_QUERY
);
549 xClose
->close( sal_True
);
552 Reference
< XComponent
> xComp( xDoc
, UNO_QUERY
);
557 catch (const com::sun::star::util::CloseVetoException
&)
561 // request is completed
562 OfficeIPCThread::RequestsCompleted( 1 );
567 if ( !aDispatches
.empty() )
569 // Execute all asynchronous dispatches now after we placed them into our request container!
570 Sequence
< PropertyValue
> aArgs( 2 );
571 aArgs
[0].Name
= "Referer";
572 aArgs
[0].Value
<<= OUString("private:OpenEvent");
573 aArgs
[1].Name
= "SynchronMode";
574 aArgs
[1].Value
<<= sal_True
;
576 for ( sal_uInt32 n
= 0; n
< aDispatches
.size(); n
++ )
578 Reference
< XDispatch
> xDispatch
= aDispatches
[n
].xDispatch
;
579 Reference
< XNotifyingDispatch
> xDisp( xDispatch
, UNO_QUERY
);
581 xDisp
->dispatchWithNotification( aDispatches
[n
].aURL
, aArgs
, this );
584 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
587 xDispatch
->dispatch( aDispatches
[n
].aURL
, aArgs
);
592 ::osl::ClearableMutexGuard
aGuard( GetMutex() );
593 bool bEmpty
= (m_nRequestCount
== 0);
596 // No more asynchronous requests?
597 // The requests are removed from the request container after they called back to this
598 // implementation via statusChanged!!
599 if ( bEmpty
&& !bNoTerminate
/*m_aRequestContainer.empty()*/ )
601 // We have to check if we have an open task otherwise we have to shutdown the office.
603 Reference
< XElementAccess
> xList( xDesktop
->getFrames(), UNO_QUERY
);
605 if ( !xList
->hasElements() )
607 // We don't have any task open so we have to shutdown ourself!!
608 return xDesktop
->terminate();
616 void SAL_CALL
DispatchWatcher::disposing( const ::com::sun::star::lang::EventObject
& )
617 throw(::com::sun::star::uno::RuntimeException
)
622 void SAL_CALL
DispatchWatcher::dispatchFinished( const DispatchResultEvent
& ) throw( RuntimeException
)
624 osl::ClearableMutexGuard
aGuard( GetMutex() );
625 sal_Int16 nCount
= --m_nRequestCount
;
627 OfficeIPCThread::RequestsCompleted( 1 );
628 if ( !nCount
&& !OfficeIPCThread::AreRequestsPending() )
630 // We have to check if we have an open task otherwise we have to shutdown the office.
631 Reference
< XDesktop2
> xDesktop
= Desktop::create( ::comphelper::getProcessComponentContext() );
632 Reference
< XElementAccess
> xList( xDesktop
->getFrames(), UNO_QUERY
);
634 if ( !xList
->hasElements() )
636 // We don't have any task open so we have to shutdown ourself!!
637 xDesktop
->terminate();
651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */