summaryrefslogtreecommitdiffstats
path: root/desktop/source/app/dispatchwatcher.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--desktop/source/app/dispatchwatcher.cxx832
1 files changed, 832 insertions, 0 deletions
diff --git a/desktop/source/app/dispatchwatcher.cxx b/desktop/source/app/dispatchwatcher.cxx
new file mode 100644
index 000000000..381507149
--- /dev/null
+++ b/desktop/source/app/dispatchwatcher.cxx
@@ -0,0 +1,832 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svl/fstathelper.hxx>
+
+#include <app.hxx>
+#include "dispatchwatcher.hxx"
+#include "officeipcthread.hxx"
+#include <rtl/ustring.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/synchronousdispatch.hxx>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/container/XContainerQuery.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/view/XPrintable.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/script/XLibraryContainer2.hpp>
+#include <com/sun/star/document/XEmbeddedScripts.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <osl/thread.hxx>
+#include <osl/file.hxx>
+#include <iostream>
+#include <string_view>
+#include <utility>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::document;
+
+namespace document = ::com::sun::star::document;
+
+namespace desktop
+{
+
+namespace {
+
+struct DispatchHolder
+{
+ DispatchHolder( URL _aURL, Reference< XDispatch > const & rDispatch ) :
+ aURL(std::move( _aURL )), xDispatch( rDispatch ) {}
+
+ URL aURL;
+ Reference< XDispatch > xDispatch;
+};
+
+std::shared_ptr<const SfxFilter> impl_lookupExportFilterForUrl( std::u16string_view rUrl, std::u16string_view rFactory )
+{
+ // create the list of filters
+ OUString sQuery = "getSortedFilterList()"
+ ":module=" +
+ OUString::Concat(rFactory) + // use long name here !
+ ":iflags=" +
+ OUString::number(static_cast<sal_Int32>(SfxFilterFlags::EXPORT)) +
+ ":eflags=" +
+ OUString::number(static_cast<int>(SFX_FILTER_NOTINSTALLED));
+
+ const Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ const Reference< XContainerQuery > xFilterFactory(
+ xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.FilterFactory", xContext ),
+ UNO_QUERY_THROW );
+
+ std::shared_ptr<const SfxFilter> pBestMatch;
+
+ const Reference< XEnumeration > xFilterEnum(
+ xFilterFactory->createSubSetEnumerationByQuery( sQuery ), UNO_SET_THROW );
+ while ( xFilterEnum->hasMoreElements() )
+ {
+ comphelper::SequenceAsHashMap aFilterProps( xFilterEnum->nextElement() );
+ const OUString aName( aFilterProps.getUnpackedValueOrDefault( "Name", OUString() ) );
+ if ( !aName.isEmpty() )
+ {
+ std::shared_ptr<const SfxFilter> pFilter( SfxFilter::GetFilterByName( aName ) );
+ if ( pFilter && pFilter->CanExport() && pFilter->GetWildcard().Matches( rUrl ) )
+ {
+ if ( !pBestMatch || ( SfxFilterFlags::PREFERED & pFilter->GetFilterFlags() ) )
+ pBestMatch = pFilter;
+ }
+ }
+ }
+
+ return pBestMatch;
+}
+
+std::shared_ptr<const SfxFilter> impl_getExportFilterFromUrl(
+ const OUString& rUrl, const OUString& rFactory)
+{
+ try
+ {
+ const Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ const Reference< document::XTypeDetection > xTypeDetector(
+ xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.document.TypeDetection", xContext ),
+ UNO_QUERY_THROW );
+ const OUString aTypeName( xTypeDetector->queryTypeByURL( rUrl ) );
+
+ std::shared_ptr<const SfxFilter> pFilter( SfxFilterMatcher( rFactory ).GetFilter4EA( aTypeName, SfxFilterFlags::EXPORT ) );
+ if ( !pFilter )
+ pFilter = impl_lookupExportFilterForUrl( rUrl, rFactory );
+ if ( !pFilter )
+ {
+ OUString aTempName;
+ FileBase::getSystemPathFromFileURL( rUrl, aTempName );
+ OString aSource = OUStringToOString ( aTempName, osl_getThreadTextEncoding() );
+ std::cerr << "Error: no export filter for " << aSource << " found, aborting." << std::endl;
+ }
+
+ return pFilter;
+ }
+ catch ( const Exception& )
+ {
+ return nullptr;
+ }
+}
+
+OUString impl_GuessFilter( const OUString& rUrlOut, const OUString& rDocService )
+{
+ OUString aOutFilter;
+ std::shared_ptr<const SfxFilter> pOutFilter = impl_getExportFilterFromUrl( rUrlOut, rDocService );
+ if (pOutFilter)
+ aOutFilter = pOutFilter->GetFilterName();
+
+ return aOutFilter;
+}
+
+/// dump scripts in a document to the console.
+void scriptCat(const Reference< XModel >& xDoc )
+{
+ Reference< XEmbeddedScripts > xScriptAccess( xDoc, UNO_QUERY );
+ if (!xScriptAccess)
+ {
+ std::cout << "No script access\n";
+ return;
+ }
+
+ // ignore xScriptAccess->getDialogLibraries() for now
+ Reference< css::script::XLibraryContainer2 > xLibraries(
+ xScriptAccess->getBasicLibraries() );
+
+ if ( !xLibraries.is() )
+ {
+ std::cout << "No script libraries\n";
+ return;
+ }
+
+ const Sequence< OUString > aLibNames = xLibraries->getElementNames();
+ std::cout << "Libraries: " << aLibNames.getLength() << "\n";
+ for (OUString const & libName : aLibNames)
+ {
+ std::cout << "Library: '" << libName << "' children: ";
+ Reference< XNameContainer > xContainer;
+ try {
+ if (!xLibraries->isLibraryLoaded( libName ))
+ xLibraries->loadLibrary( libName );
+ xContainer = Reference< XNameContainer >(
+ xLibraries->getByName( libName ), UNO_QUERY );
+ }
+ catch (const css::uno::Exception &e)
+ {
+ std::cout << "[" << libName << "] - failed to load library: " << e.Message << "\n";
+ continue;
+ }
+ if( !xContainer.is() )
+ std::cout << "0\n";
+ else
+ {
+ Sequence< OUString > aObjectNames = xContainer->getElementNames();
+
+ std::cout << aObjectNames.getLength() << "\n\n";
+ for ( sal_Int32 j = 0 ; j < aObjectNames.getLength() ; ++j )
+ {
+ const OUString &rObjectName = aObjectNames[j];
+
+ try
+ {
+ Any aCode = xContainer->getByName( rObjectName );
+ OUString aCodeString;
+
+ if (! (aCode >>= aCodeString ) )
+ std::cout << "[" << rObjectName << "] - error fetching code\n";
+ else
+ std::cout << "[" << rObjectName << "]\n"
+ << aCodeString.trim()
+ << "\n[/" << rObjectName << "]\n";
+ }
+ catch (const css::uno::Exception &e)
+ {
+ std::cout << "[" << rObjectName << "] - exception " << e.Message << " fetching code\n";
+ }
+
+ if (j < aObjectNames.getLength() - 1)
+ std::cout << "\n----------------------------------------------------------\n";
+ std::cout << "\n";
+ }
+ }
+ }
+}
+
+// Perform batch print
+void batchPrint( std::u16string_view rPrinterName, const Reference< XPrintable > &xDoc,
+ const INetURLObject &aObj, const OUString &aName )
+{
+ OUString aFilterOut;
+ OUString aPrinterName;
+ size_t nPathIndex = rPrinterName.rfind( ';' );
+ if( nPathIndex != std::u16string_view::npos )
+ aFilterOut=rPrinterName.substr( nPathIndex+1 );
+ if( nPathIndex != 0 )
+ aPrinterName=rPrinterName.substr( 0, nPathIndex );
+
+ INetURLObject aOutFilename( aObj );
+ aOutFilename.SetExtension( u"pdf" );
+ FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
+ OUString aOutFile = aFilterOut + "/" + aOutFilename.getName();
+
+ OUString aTempName;
+ FileBase::getSystemPathFromFileURL( aName, aTempName );
+ OString aSource8 = OUStringToOString ( aTempName, osl_getThreadTextEncoding() );
+ FileBase::getSystemPathFromFileURL( aOutFile, aTempName );
+ OString aTargetURL8 = OUStringToOString(aTempName, osl_getThreadTextEncoding() );
+
+ std::cout << "print " << aSource8 << " -> " << aTargetURL8;
+ std::cout << " using " << (aPrinterName.isEmpty() ? "<default_printer>" : OUStringToOString( aPrinterName, osl_getThreadTextEncoding() ));
+ std::cout << std::endl;
+
+ // create the custom printer, if given
+ Sequence < PropertyValue > aPrinterArgs;
+ if( !aPrinterName.isEmpty() )
+ {
+ aPrinterArgs = { comphelper::makePropertyValue("Name", aPrinterName) };
+ xDoc->setPrinter( aPrinterArgs );
+ }
+
+ // print ( also without user interaction )
+ aPrinterArgs = { comphelper::makePropertyValue("FileName", aOutFile),
+ comphelper::makePropertyValue("Wait", true) };
+ xDoc->print( aPrinterArgs );
+}
+
+} // anonymous namespace
+
+DispatchWatcher::DispatchWatcher()
+ : m_nRequestCount(0)
+{
+}
+
+
+DispatchWatcher::~DispatchWatcher()
+{
+}
+
+
+bool DispatchWatcher::executeDispatchRequests( const std::vector<DispatchRequest>& aDispatchRequestsList, bool bNoTerminate )
+{
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+
+ std::vector< DispatchHolder > aDispatches;
+ bool bSetInputFilter = false;
+ OUString aForcedInputFilter;
+
+ for (auto const & aDispatchRequest: aDispatchRequestsList)
+ {
+ // Set Input Filter
+ if ( aDispatchRequest.aRequestType == REQUEST_INFILTER )
+ {
+ bSetInputFilter = true;
+ aForcedInputFilter = aDispatchRequest.aURL;
+ RequestHandler::RequestsCompleted();
+ continue;
+ }
+
+ // create parameter array
+ std::vector<PropertyValue> aArgs;
+
+ // mark request as user interaction from outside
+ aArgs.emplace_back("Referer", 0, Any(OUString("private:OpenEvent")),
+ PropertyState_DIRECT_VALUE);
+
+ OUString aTarget("_default");
+
+ if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
+ aDispatchRequest.aRequestType == REQUEST_PRINTTO ||
+ aDispatchRequest.aRequestType == REQUEST_BATCHPRINT ||
+ aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
+ aDispatchRequest.aRequestType == REQUEST_CAT ||
+ aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT)
+ {
+ // documents opened for printing are opened readonly because they must be opened as a
+ // new document and this document could be open already
+ aArgs.emplace_back("ReadOnly", 0, Any(true), PropertyState_DIRECT_VALUE);
+ // always open a new document for printing, because it must be disposed afterwards
+ aArgs.emplace_back("OpenNewView", 0, Any(true), PropertyState_DIRECT_VALUE);
+ // printing is done in a hidden view
+ aArgs.emplace_back("Hidden", 0, Any(true), PropertyState_DIRECT_VALUE);
+ // load document for printing without user interaction
+ aArgs.emplace_back("Silent", 0, Any(true), PropertyState_DIRECT_VALUE);
+
+ // hidden documents should never be put into open tasks
+ aTarget = "_blank";
+ }
+ else
+ {
+ Reference < XInteractionHandler2 > xInteraction(
+ InteractionHandler::createWithParent(::comphelper::getProcessComponentContext(), nullptr) );
+
+ aArgs.emplace_back("InteractionHandler", 0, Any(xInteraction),
+ PropertyState_DIRECT_VALUE);
+
+ aArgs.emplace_back("MacroExecutionMode", 0,
+ Any(css::document::MacroExecMode::USE_CONFIG),
+ PropertyState_DIRECT_VALUE);
+
+ aArgs.emplace_back("UpdateDocMode", 0,
+ Any(css::document::UpdateDocMode::ACCORDING_TO_CONFIG),
+ PropertyState_DIRECT_VALUE);
+ }
+
+ if ( !aDispatchRequest.aPreselectedFactory.isEmpty() )
+ {
+ aArgs.emplace_back(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, 0,
+ Any(aDispatchRequest.aPreselectedFactory),
+ PropertyState_DIRECT_VALUE);
+ }
+
+ OUString aName( GetURL_Impl( aDispatchRequest.aURL, aDispatchRequest.aCwdUrl ) );
+
+ // load the document ... if they are loadable!
+ // Otherwise try to dispatch it ...
+ Reference < XPrintable > xDoc;
+ if(
+ ( aName.startsWith( ".uno" ) ) ||
+ ( aName.startsWith( "slot:" ) ) ||
+ ( aName.startsWith( "macro:" ) ) ||
+ ( aName.startsWith("vnd.sun.star.script") )
+ )
+ {
+ // Attention: URL must be parsed full. Otherwise some detections on it will fail!
+ // It doesn't matter, if parser isn't available. Because; We try loading of URL then ...
+ URL aURL ;
+ aURL.Complete = aName;
+
+ Reference < XDispatch > xDispatcher ;
+ Reference < XURLTransformer > xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+
+ if( xParser.is() )
+ xParser->parseStrict( aURL );
+
+ xDispatcher = xDesktop->queryDispatch( aURL, OUString(), 0 );
+ SAL_WARN_IF(
+ !xDispatcher.is(), "desktop.app",
+ "unsupported dispatch request <" << aName << ">");
+ if( xDispatcher.is() )
+ {
+ {
+ osl::MutexGuard aGuard(m_mutex);
+ // Remember request so we can find it in statusChanged!
+ m_nRequestCount++;
+ }
+
+ // Use local vector to store dispatcher because we have to fill our request container before
+ // we can dispatch. Otherwise it would be possible that statusChanged is called before we dispatched all requests!!
+ aDispatches.emplace_back( aURL, xDispatcher );
+ }
+ }
+ else if ( aName.startsWith( "service:" ) )
+ {
+ // TODO: the dispatch has to be done for loadComponentFromURL as well.
+ URL aURL ;
+ aURL.Complete = aName;
+
+ Reference < XDispatch > xDispatcher ;
+ Reference < XURLTransformer > xParser ( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+
+ if( xParser.is() )
+ xParser->parseStrict( aURL );
+
+ xDispatcher = xDesktop->queryDispatch( aURL, OUString(), 0 );
+
+ if( xDispatcher.is() )
+ {
+ try
+ {
+ // We have to be listener to catch errors during dispatching URLs.
+ // Otherwise it would be possible to have an office running without an open
+ // window!!
+ Sequence < PropertyValue > aArgs2{ comphelper::makePropertyValue("SynchronMode",
+ true) };
+ Reference < XNotifyingDispatch > xDisp( xDispatcher, UNO_QUERY );
+ if ( xDisp.is() )
+ xDisp->dispatchWithNotification( aURL, aArgs2, this );
+ else
+ xDispatcher->dispatch( aURL, aArgs2 );
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.app",
+ "Desktop::OpenDefault() ignoring Exception while calling XNotifyingDispatch");
+ }
+ }
+ }
+ else
+ {
+ INetURLObject aObj( aName );
+ if ( aObj.GetProtocol() == INetProtocol::PrivSoffice )
+ aTarget = "_default";
+
+ // Set "AsTemplate" argument according to request type
+ if ( aDispatchRequest.aRequestType == REQUEST_FORCENEW ||
+ aDispatchRequest.aRequestType == REQUEST_FORCEOPEN )
+ {
+ aArgs.emplace_back("AsTemplate", 0,
+ Any(aDispatchRequest.aRequestType == REQUEST_FORCENEW),
+ PropertyState_DIRECT_VALUE);
+ }
+
+ // if we are called in viewmode, open document read-only
+ if(aDispatchRequest.aRequestType == REQUEST_VIEW) {
+ aArgs.emplace_back("ReadOnly", 0, Any(true), PropertyState_DIRECT_VALUE);
+ }
+
+ // if we are called with --show set Start in mediadescriptor
+ if(aDispatchRequest.aRequestType == REQUEST_START) {
+ aArgs.emplace_back("StartPresentation", 0, Any(true), PropertyState_DIRECT_VALUE);
+ }
+
+ // Force input filter, if possible
+ if( bSetInputFilter )
+ {
+ sal_Int32 nFilterOptionsIndex = 0;
+ aArgs.emplace_back("FilterName", 0,
+ Any(aForcedInputFilter.getToken(0, ':', nFilterOptionsIndex)),
+ PropertyState_DIRECT_VALUE);
+
+ if (0 < nFilterOptionsIndex)
+ {
+ aArgs.emplace_back("FilterOptions", 0,
+ Any(aForcedInputFilter.copy(nFilterOptionsIndex)),
+ PropertyState_DIRECT_VALUE);
+ }
+ }
+
+ // This is a synchron loading of a component so we don't have to deal with our statusChanged listener mechanism.
+ try
+ {
+ xDoc.set(comphelper::SynchronousDispatch::dispatch(
+ xDesktop, aName, aTarget, comphelper::containerToSequence(aArgs)),
+ UNO_QUERY);
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.app",
+ "Dispatchwatcher IllegalArgumentException while calling loadComponentFromURL");
+ }
+ catch (const css::io::IOException&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "desktop.app",
+ "Dispatchwatcher IOException while calling loadComponentFromURL");
+ }
+ if ( aDispatchRequest.aRequestType == REQUEST_OPEN ||
+ aDispatchRequest.aRequestType == REQUEST_VIEW ||
+ aDispatchRequest.aRequestType == REQUEST_START ||
+ aDispatchRequest.aRequestType == REQUEST_FORCEOPEN ||
+ aDispatchRequest.aRequestType == REQUEST_FORCENEW )
+ {
+ // request is completed
+ RequestHandler::RequestsCompleted();
+ }
+ else if ( aDispatchRequest.aRequestType == REQUEST_PRINT ||
+ aDispatchRequest.aRequestType == REQUEST_PRINTTO ||
+ aDispatchRequest.aRequestType == REQUEST_BATCHPRINT ||
+ aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
+ aDispatchRequest.aRequestType == REQUEST_CAT ||
+ aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT )
+ {
+ if ( xDoc.is() )
+ {
+ // Do we need to save the document in a different format?
+ if ( aDispatchRequest.aRequestType == REQUEST_CONVERSION ||
+ aDispatchRequest.aRequestType == REQUEST_CAT )
+ {
+// FIXME: factor out into a method ...
+ Reference< XStorable > xStorable( xDoc, UNO_QUERY );
+ if ( xStorable.is() ) {
+ OUString aParam = aDispatchRequest.aPrinterName;
+ sal_Int32 nPathIndex = aParam.lastIndexOf( ';' );
+ sal_Int32 nFilterIndex = aParam.indexOf( ':' );
+ sal_Int32 nImgFilterIndex = aParam.lastIndexOf( '|' );
+ if( nPathIndex < nFilterIndex )
+ nFilterIndex = -1;
+
+ OUString aFilterOut;
+ OUString aImgOut;
+ OUString aFilter;
+ OUString aFilterExt;
+ bool bGuess = false;
+
+ if( nFilterIndex >= 0 )
+ {
+ aFilter = aParam.copy( nFilterIndex+1, nPathIndex-nFilterIndex-1 );
+ aFilterExt = aParam.copy( 0, nFilterIndex );
+ }
+ else
+ {
+ // Guess
+ bGuess = true;
+ aFilterExt = aParam.copy( 0, nPathIndex );
+ }
+
+ if( nImgFilterIndex >= 0 )
+ {
+ aImgOut = aParam.copy( nImgFilterIndex+1 );
+ aFilterOut = aParam.copy( nPathIndex+1, nImgFilterIndex-nPathIndex-1 );
+ }
+ else
+ aFilterOut = aParam.copy( nPathIndex+1 );
+
+ FileBase::getFileURLFromSystemPath( aFilterOut, aFilterOut );
+ INetURLObject aOutFilename(aFilterOut);
+ aOutFilename.Append(aObj.getName(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::NONE));
+ aOutFilename.SetExtension(aFilterExt);
+ OUString aOutFile
+ = aOutFilename.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ std::unique_ptr<utl::TempFile> fileForCat;
+ if( aDispatchRequest.aRequestType == REQUEST_CAT )
+ {
+ fileForCat = std::make_unique<utl::TempFile>();
+ if (fileForCat->IsValid())
+ fileForCat->EnableKillingFile();
+ else
+ std::cerr << "Error: Cannot create temporary file..." << std::endl ;
+ aOutFile = fileForCat->GetURL();
+ }
+
+ if ( bGuess )
+ {
+ OUString aDocService;
+ Reference< XModel > xModel( xDoc, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ utl::MediaDescriptor aMediaDesc( xModel->getArgs() );
+ aDocService = aMediaDesc.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTSERVICE, OUString() );
+ }
+ aFilter = impl_GuessFilter( aOutFile, aDocService );
+ }
+
+ bool bMultiFileTarget = false;
+
+ if (aFilter.isEmpty())
+ {
+ std::cerr << "Error: no export filter" << std::endl;
+ }
+ else
+ {
+ sal_Int32 nFilterOptionsIndex = aFilter.indexOf(':');
+ sal_Int32 nProps = ( 0 < nFilterOptionsIndex ) ? 4 : 3;
+
+ if ( !aImgOut.isEmpty() )
+ nProps +=1;
+ Sequence<PropertyValue> conversionProperties( nProps );
+ auto pconversionProperties = conversionProperties.getArray();
+ pconversionProperties[0].Name = "ConversionRequestOrigin";
+ pconversionProperties[0].Value <<= OUString("CommandLine");
+ pconversionProperties[1].Name = "Overwrite";
+ pconversionProperties[1].Value <<= true;
+
+ pconversionProperties[2].Name = "FilterName";
+ if( 0 < nFilterOptionsIndex )
+ {
+ OUString sFilterName = aFilter.copy(0, nFilterOptionsIndex);
+ OUString sFilterOptions = aFilter.copy(nFilterOptionsIndex + 1);
+
+ if (sFilterName == "Text - txt - csv (StarCalc)")
+ {
+ sal_Int32 nIdx(0);
+ // If the 11th token is '-1' then we export a file
+ // per sheet where the file name is based on the suggested
+ // output filename concatenated with the sheet name, so adjust
+ // the output and overwrite messages
+ // If the 11th token is not present or numeric 0 then the
+ // default sheet is exported with the output filename. If it
+ // is numeric >0 then that sheet (1-based) with the output
+ // filename concatenated with the sheet name. So even if
+ // that is a single file, the multi file target mechanism is
+ // used.
+ const OUString aTok(sFilterOptions.getToken(11, ',', nIdx));
+ // Actual validity is checked in Calc, here just check for
+ // presence of numeric value at start.
+ bMultiFileTarget = (!aTok.isEmpty() && aTok.toInt32() != 0);
+ }
+
+ pconversionProperties[2].Value <<= sFilterName;
+
+ pconversionProperties[3].Name = "FilterOptions";
+ pconversionProperties[3].Value <<= sFilterOptions;
+ }
+ else
+ {
+ pconversionProperties[2].Value <<= aFilter;
+ }
+
+ if ( !aImgOut.isEmpty() )
+ {
+ assert(conversionProperties[nProps-1].Name.isEmpty());
+ pconversionProperties[nProps-1].Name = "ImageFilter";
+ pconversionProperties[nProps-1].Value <<= aImgOut;
+ }
+
+ OUString aTempName;
+ FileBase::getSystemPathFromFileURL(aName, aTempName);
+ OString aSource8 = OUStringToOString(aTempName, osl_getThreadTextEncoding());
+ FileBase::getSystemPathFromFileURL(aOutFile, aTempName);
+ OString aTargetURL8 = OUStringToOString(aTempName, osl_getThreadTextEncoding());
+ if (aDispatchRequest.aRequestType != REQUEST_CAT)
+ {
+ std::cout << "convert " << aSource8;
+ if (!bMultiFileTarget)
+ std::cout << " -> " << aTargetURL8;
+ std::cout << " using filter : " << OUStringToOString(aFilter, osl_getThreadTextEncoding()) << std::endl;
+ if (!bMultiFileTarget && FStatHelper::IsDocument(aOutFile))
+ std::cout << "Overwriting: " << OUStringToOString(aTempName, osl_getThreadTextEncoding()) << std::endl ;
+ }
+ try
+ {
+ xStorable->storeToURL(aOutFile, conversionProperties);
+ }
+ catch (const Exception& rException)
+ {
+ std::cerr << "Error: Please verify input parameters...";
+ if (!rException.Message.isEmpty())
+ std::cerr << " (" << rException.Message << ")";
+ std::cerr << std::endl;
+ }
+
+ if (fileForCat && fileForCat->IsValid())
+ {
+ SvStream* aStream = fileForCat->GetStream(StreamMode::STD_READ);
+ while (aStream->good())
+ {
+ OString aStr;
+ aStream->ReadLine(aStr, SAL_MAX_INT32);
+ for (sal_Int32 i = 0; i < aStr.getLength(); ++i)
+ {
+ std::cout << aStr[i];
+ }
+ std::cout << std::endl;
+ }
+ }
+ }
+ }
+ }
+ else if ( aDispatchRequest.aRequestType == REQUEST_SCRIPT_CAT )
+ {
+ Reference< XModel > xModel( xDoc, UNO_QUERY );
+ if( xModel.is() )
+ scriptCat( xModel );
+ }
+ else if ( aDispatchRequest.aRequestType == REQUEST_BATCHPRINT )
+ {
+ batchPrint( aDispatchRequest.aPrinterName, xDoc, aObj, aName );
+ }
+ else
+ {
+ if ( aDispatchRequest.aRequestType == REQUEST_PRINTTO )
+ {
+ // create the printer
+ Sequence < PropertyValue > aPrinterArgs{ comphelper::makePropertyValue(
+ "Name", aDispatchRequest.aPrinterName) };
+ xDoc->setPrinter( aPrinterArgs );
+ }
+
+ // print ( also without user interaction )
+ Sequence < PropertyValue > aPrinterArgs{ comphelper::makePropertyValue("Wait",
+ true) };
+ xDoc->print( aPrinterArgs );
+ }
+ }
+ else
+ {
+ std::cerr << "Error: source file could not be loaded" << std::endl;
+ }
+
+ // remove the document
+ try
+ {
+ Reference < XCloseable > xClose( xDoc, UNO_QUERY );
+ if ( xClose.is() )
+ xClose->close( true );
+ else
+ {
+ Reference < XComponent > xComp( xDoc, UNO_QUERY );
+ if ( xComp.is() )
+ xComp->dispose();
+ }
+ }
+ catch (const css::util::CloseVetoException&)
+ {
+ }
+
+ // request is completed
+ RequestHandler::RequestsCompleted();
+ }
+ }
+ }
+
+ if ( !aDispatches.empty() )
+ {
+ // Execute all asynchronous dispatches now after we placed them into our request container!
+ Sequence < PropertyValue > aArgs{
+ comphelper::makePropertyValue("Referer", OUString("private:OpenEvent")),
+ comphelper::makePropertyValue("SynchronMode", true)
+ };
+
+ for (const DispatchHolder & aDispatche : aDispatches)
+ {
+ Reference< XDispatch > xDispatch = aDispatche.xDispatch;
+ Reference < XNotifyingDispatch > xDisp( xDispatch, UNO_QUERY );
+ if ( xDisp.is() )
+ xDisp->dispatchWithNotification( aDispatche.aURL, aArgs, this );
+ else
+ {
+ {
+ osl::MutexGuard aGuard(m_mutex);
+ m_nRequestCount--;
+ }
+ xDispatch->dispatch( aDispatche.aURL, aArgs );
+ }
+ }
+ }
+
+ ::osl::ClearableMutexGuard aGuard(m_mutex);
+ bool bEmpty = (m_nRequestCount == 0);
+ aGuard.clear();
+
+ // No more asynchronous requests?
+ // The requests are removed from the request container after they called back to this
+ // implementation via statusChanged!!
+ if ( bEmpty && !bNoTerminate /*m_aRequestContainer.empty()*/ )
+ {
+ // We have to check if we have an open task otherwise we have to shutdown the office.
+ Reference< XElementAccess > xList = xDesktop->getFrames();
+
+ if ( !xList->hasElements() )
+ {
+ // We don't have any task open so we have to shutdown ourself!!
+ return xDesktop->terminate();
+ }
+ }
+
+ return false;
+}
+
+
+void SAL_CALL DispatchWatcher::disposing( const css::lang::EventObject& )
+{
+}
+
+
+void SAL_CALL DispatchWatcher::dispatchFinished( const DispatchResultEvent& )
+{
+ osl::ClearableMutexGuard aGuard(m_mutex);
+ sal_Int16 nCount = --m_nRequestCount;
+ aGuard.clear();
+ RequestHandler::RequestsCompleted();
+ if ( !nCount && !RequestHandler::AreRequestsPending() )
+ {
+ // We have to check if we have an open task otherwise we have to shutdown the office.
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XElementAccess > xList = xDesktop->getFrames();
+
+ if ( !xList->hasElements() )
+ {
+ // We don't have any task open so we have to shutdown ourself!!
+ xDesktop->terminate();
+ }
+ }
+}
+
+} // namespace desktop
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */