GPU-Calc: remove Alloc_Host_Ptr for clmem of NAN vector
[LibreOffice.git] / desktop / source / app / dispatchwatcher.cxx
blobc33332ca64b2a956065d5fdeab128eab8b0bc5b1
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 <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>
53 #include <vector>
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;
67 namespace desktop
70 OUString GetURL_Impl(
71 const OUString& rName, boost::optional< OUString > const & cwdUrl );
73 struct DispatchHolder
75 DispatchHolder( const URL& rURL, Reference< XDispatch >& rDispatch ) :
76 aURL( rURL ), xDispatch( rDispatch ) {}
78 URL aURL;
79 OUString cwdUrl;
80 Reference< XDispatch > xDispatch;
83 static OUString impl_GetFilterFromExt( OUString aUrl, SfxFilterFlags nFlags,
84 OUString aAppl )
86 OUString aFilter;
87 SfxMedium* pMedium = new SfxMedium( aUrl,
88 STREAM_STD_READ );
90 const SfxFilter *pSfxFilter = NULL;
91 if( nFlags == SFX_FILTER_EXPORT )
93 SfxFilterMatcher( aAppl ).GuessFilterIgnoringContent( *pMedium, &pSfxFilter, nFlags, 0 );
95 else
97 SFX_APP()->GetFilterMatcher().GuessFilter( *pMedium, &pSfxFilter, nFlags, 0 );
100 if( pSfxFilter )
102 if (nFlags == SFX_FILTER_EXPORT)
103 aFilter = pSfxFilter->GetFilterName();
104 else
105 aFilter = pSfxFilter->GetServiceName();
108 delete pMedium;
109 return aFilter;
111 static OUString impl_GuessFilter( OUString aUrlIn, OUString aUrlOut )
113 /* aAppl can also be set to Factory like scalc, swriter... */
114 OUString aAppl;
115 aAppl = impl_GetFilterFromExt( aUrlIn, SFX_FILTER_IMPORT, aAppl );
116 return impl_GetFilterFromExt( aUrlOut, SFX_FILTER_EXPORT, aAppl );
119 namespace
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()
154 : m_nRequestCount(0)
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() )
181 nCount++;
183 // Set Input Filter
184 if ( aDispatchRequest.aRequestType == REQUEST_INFILTER )
186 bSetInputFilter = sal_True;
187 aForcedInputFilter = aDispatchRequest.aURL;
188 OfficeIPCThread::RequestsCompleted( 1 );
189 continue;
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)
197 nCount++;
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";
215 else
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
260 aTarget = "_blank";
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 ...
274 URL aURL ;
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 ) );
291 m_nRequestCount++;
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.
302 URL aURL ;
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
319 // window!!
320 Sequence < PropertyValue > aArgs2(1);
321 aArgs2[0].Name = "SynchronMode";
322 aArgs2[0].Value <<= sal_True;
323 Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY );
324 if ( xDisp.is() )
325 xDisp->dispatchWithNotification( aURL, aArgs2, DispatchWatcher::GetDispatchWatcher() );
326 else
327 xDispatcher->dispatch( aURL, aArgs2 );
329 catch (const ::com::sun::star::uno::Exception& e)
331 SAL_WARN(
332 "desktop.app",
333 "Desktop::OpenDefault() ignoring Exception while"
334 " calling XNotifyingDispatch: \"" << e.Message
335 << "\"");
339 else
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;
354 else
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)
390 SAL_WARN(
391 "desktop.app",
392 "Dispatchwatcher IllegalArgumentException while calling"
393 " loadComponentFromURL: \"" << iae.Message << "\"");
395 catch (const com::sun::star::io::IOException& ioe)
397 SAL_WARN(
398 "desktop.app",
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 )
416 if ( xDoc.is() )
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 )
425 nFilterIndex = -1;
426 OUString aFilterOut=aParam.copy( nPathIndex+1 );
427 OUString aFilter;
428 OUString aFilterExt;
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 );
436 else
438 // Guess
439 bGuess = sal_True;
440 aFilterExt = aParam.copy( 0, nPathIndex );
442 INetURLObject aOutFilename( aObj );
443 aOutFilename.SetExtension( aFilterExt );
444 FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
445 OUString aOutFile = aFilterOut+
446 "/" +
447 aOutFilename.getName();
449 if ( bGuess )
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;
461 OUString aTempName;
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( ';' );
483 OUString aFilterOut;
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+
494 "/" +
495 aOutFilename.getName();
497 OUString aTempName;
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 );
522 } else {
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 );
539 else
541 // place error message here ...
544 // remove the document
547 Reference < XCloseable > xClose( xDoc, UNO_QUERY );
548 if ( xClose.is() )
549 xClose->close( sal_True );
550 else
552 Reference < XComponent > xComp( xDoc, UNO_QUERY );
553 if ( xComp.is() )
554 xComp->dispose();
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 );
580 if ( xDisp.is() )
581 xDisp->dispatchWithNotification( aDispatches[n].aURL, aArgs, this );
582 else
584 ::osl::ClearableMutexGuard aGuard( GetMutex() );
585 m_nRequestCount--;
586 aGuard.clear();
587 xDispatch->dispatch( aDispatches[n].aURL, aArgs );
592 ::osl::ClearableMutexGuard aGuard( GetMutex() );
593 bool bEmpty = (m_nRequestCount == 0);
594 aGuard.clear();
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.
602 aGuard.clear();
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();
612 return sal_False;
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;
626 aGuard.clear();
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: */