diff options
Diffstat (limited to 'vcl/source/gdi/print3.cxx')
-rw-r--r-- | vcl/source/gdi/print3.cxx | 2146 |
1 files changed, 2146 insertions, 0 deletions
diff --git a/vcl/source/gdi/print3.cxx b/vcl/source/gdi/print3.cxx new file mode 100644 index 000000000..c02023dd1 --- /dev/null +++ b/vcl/source/gdi/print3.cxx @@ -0,0 +1,2146 @@ +/* -*- 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/types.h> +#include <sal/log.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <o3tl/safeint.hxx> +#include <tools/diagnose_ex.h> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> + +#include <vcl/metaact.hxx> +#include <vcl/print.hxx> +#include <vcl/printer/Options.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <configsettings.hxx> +#include <printdlg.hxx> +#include <salinst.hxx> +#include <salprn.hxx> +#include <strings.hrc> +#include <svdata.hxx> + +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ui/dialogs/FilePicker.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/view/DuplexMode.hpp> + +#include <unordered_map> +#include <unordered_set> + +using namespace vcl; + +namespace { + +class ImplPageCache +{ + struct CacheEntry + { + GDIMetaFile aPage; + PrinterController::PageSize aSize; + }; + + std::vector< CacheEntry > maPages; + std::vector< sal_Int32 > maPageNumbers; + std::vector< sal_Int32 > maCacheRanking; + + static const sal_Int32 nCacheSize = 6; + + void updateRanking( sal_Int32 nLastHit ) + { + if( maCacheRanking[0] != nLastHit ) + { + for( sal_Int32 i = nCacheSize-1; i > 0; i-- ) + maCacheRanking[i] = maCacheRanking[i-1]; + maCacheRanking[0] = nLastHit; + } + } + +public: + ImplPageCache() + : maPages( nCacheSize ) + , maPageNumbers( nCacheSize, -1 ) + , maCacheRanking( nCacheSize ) + { + for( sal_Int32 i = 0; i < nCacheSize; i++ ) + maCacheRanking[i] = nCacheSize - i - 1; + } + + // caution: does not ensure uniqueness + void insert( sal_Int32 i_nPageNo, const GDIMetaFile& i_rPage, const PrinterController::PageSize& i_rSize ) + { + sal_Int32 nReplacePage = maCacheRanking.back(); + maPages[ nReplacePage ].aPage = i_rPage; + maPages[ nReplacePage ].aSize = i_rSize; + maPageNumbers[ nReplacePage ] = i_nPageNo; + // cache insertion means in our case, the page was just queried + // so update the ranking + updateRanking( nReplacePage ); + } + + // caution: bad algorithm; should there ever be reason to increase the cache size beyond 6 + // this needs to be urgently rewritten. However do NOT increase the cache size lightly, + // whole pages can be rather memory intensive + bool get( sal_Int32 i_nPageNo, GDIMetaFile& o_rPageFile, PrinterController::PageSize& o_rSize ) + { + for( sal_Int32 i = 0; i < nCacheSize; ++i ) + { + if( maPageNumbers[i] == i_nPageNo ) + { + updateRanking( i ); + o_rPageFile = maPages[i].aPage; + o_rSize = maPages[i].aSize; + return true; + } + } + return false; + } + + void invalidate() + { + for( sal_Int32 i = 0; i < nCacheSize; ++i ) + { + maPageNumbers[i] = -1; + maPages[i].aPage.Clear(); + maCacheRanking[i] = nCacheSize - i - 1; + } + } +}; + +} + +class vcl::ImplPrinterControllerData +{ +public: + struct ControlDependency + { + OUString maDependsOnName; + sal_Int32 mnDependsOnEntry; + + ControlDependency() : mnDependsOnEntry( -1 ) {} + }; + + typedef std::unordered_map< OUString, size_t > PropertyToIndexMap; + typedef std::unordered_map< OUString, ControlDependency > ControlDependencyMap; + typedef std::unordered_map< OUString, css::uno::Sequence< sal_Bool > > ChoiceDisableMap; + + VclPtr< Printer > mxPrinter; + weld::Window* mpWindow; + css::uno::Sequence< css::beans::PropertyValue > maUIOptions; + std::vector< css::beans::PropertyValue > maUIProperties; + std::vector< bool > maUIPropertyEnabled; + PropertyToIndexMap maPropertyToIndex; + ControlDependencyMap maControlDependencies; + ChoiceDisableMap maChoiceDisableMap; + bool mbFirstPage; + bool mbLastPage; + bool mbReversePageOrder; + bool mbPapersizeFromSetup; + bool mbPapersizeFromUser; + bool mbOrientationFromUser; + bool mbPrinterModified; + css::view::PrintableState meJobState; + + vcl::PrinterController::MultiPageSetup maMultiPage; + + std::shared_ptr<vcl::PrintProgressDialog> mxProgress; + + ImplPageCache maPageCache; + + // set by user through printer properties subdialog of printer settings dialog + Size maDefaultPageSize; + // set by user through print dialog + Size maUserPageSize; + // set by user through print dialog + Orientation meUserOrientation; + // set by user through printer properties subdialog of printer settings dialog + sal_Int32 mnDefaultPaperBin; + // Set by user through printer properties subdialog of print dialog. + // Overrides application-set tray for a page. + sal_Int32 mnFixedPaperBin; + + // N.B. Apparently we have three levels of paper tray settings + // (latter overrides former): + // 1. default tray + // 2. tray set for a concrete page by an application, e.g., writer + // allows setting a printer tray (for the default printer) for a + // page style. This setting can be overridden by user by selecting + // "Use only paper tray from printer preferences" on the Options + // page in the print dialog, in which case the default tray is + // used for all pages. + // 3. tray set in printer properties the printer dialog + // I'm not quite sure why 1. and 3. are distinct, but the commit + // history suggests this is intentional... + + ImplPrinterControllerData() : + mpWindow( nullptr ), + mbFirstPage( true ), + mbLastPage( false ), + mbReversePageOrder( false ), + mbPapersizeFromSetup( false ), + mbPapersizeFromUser( false ), + mbOrientationFromUser( false ), + mbPrinterModified( false ), + meJobState( css::view::PrintableState_JOB_STARTED ), + meUserOrientation( Orientation::Portrait ), + mnDefaultPaperBin( -1 ), + mnFixedPaperBin( -1 ) + {} + + ~ImplPrinterControllerData() + { + if (mxProgress) + { + mxProgress->response(RET_CANCEL); + mxProgress.reset(); + } + } + + Size getRealPaperSize( const Size& i_rPageSize, bool bNoNUP ) const + { + Size size; + if ( mbPapersizeFromUser ) + size = maUserPageSize; + else if( mbPapersizeFromSetup ) + size = maDefaultPageSize; + else if( maMultiPage.nRows * maMultiPage.nColumns > 1 && ! bNoNUP ) + size = maMultiPage.aPaperSize; + else + size = i_rPageSize; + if(mbOrientationFromUser) + { + if ( (meUserOrientation == Orientation::Portrait && size.Width() > size.Height()) || + (meUserOrientation == Orientation::Landscape && size.Width() < size.Height()) ) + { + // coverity[swapped_arguments : FALSE] - this is in the correct order + size = Size( size.Height(), size.Width() ); + } + } + return size; + } + PrinterController::PageSize modifyJobSetup( const css::uno::Sequence< css::beans::PropertyValue >& i_rProps ); + void resetPaperToLastConfigured(); +}; + +PrinterController::PrinterController(const VclPtr<Printer>& i_xPrinter, weld::Window* i_pWindow) + : mpImplData( new ImplPrinterControllerData ) +{ + mpImplData->mxPrinter = i_xPrinter; + mpImplData->mpWindow = i_pWindow; +} + +static OUString queryFile( Printer const * pPrinter ) +{ + OUString aResult; + + css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + css::uno::Reference< css::ui::dialogs::XFilePicker3 > xFilePicker = css::ui::dialogs::FilePicker::createWithMode(xContext, css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION); + + try + { +#ifdef UNX + // add PostScript and PDF + bool bPS = true, bPDF = true; + if( pPrinter ) + { + if( pPrinter->GetCapabilities( PrinterCapType::PDF ) ) + bPS = false; + else + bPDF = false; + } + if( bPS ) + xFilePicker->appendFilter( "PostScript", "*.ps" ); + if( bPDF ) + xFilePicker->appendFilter( "Portable Document Format", "*.pdf" ); +#elif defined _WIN32 + (void)pPrinter; + xFilePicker->appendFilter( "*.PRN", "*.prn" ); +#endif + // add arbitrary files + xFilePicker->appendFilter(VclResId(SV_STDTEXT_ALLFILETYPES), "*.*"); + } + catch (const css::lang::IllegalArgumentException&) + { + TOOLS_WARN_EXCEPTION( "vcl.gdi", "caught IllegalArgumentException when registering filter" ); + } + + if( xFilePicker->execute() == css::ui::dialogs::ExecutableDialogResults::OK ) + { + css::uno::Sequence< OUString > aPathSeq( xFilePicker->getSelectedFiles() ); + INetURLObject aObj( aPathSeq[0] ); + aResult = aObj.PathToFileName(); + } + return aResult; +} + +namespace { + +struct PrintJobAsync +{ + std::shared_ptr<PrinterController> mxController; + JobSetup maInitSetup; + + PrintJobAsync(const std::shared_ptr<PrinterController>& i_xController, + const JobSetup& i_rInitSetup) + : mxController( i_xController ), maInitSetup( i_rInitSetup ) + {} + + DECL_LINK( ExecJob, void*, void ); +}; + +} + +IMPL_LINK_NOARG(PrintJobAsync, ExecJob, void*, void) +{ + Printer::ImplPrintJob(mxController, maInitSetup); + + // clean up, do not access members after this + delete this; +} + +void Printer::PrintJob(const std::shared_ptr<PrinterController>& i_xController, + const JobSetup& i_rInitSetup) +{ + bool bSynchronous = false; + css::beans::PropertyValue* pVal = i_xController->getValue( "Wait" ); + if( pVal ) + pVal->Value >>= bSynchronous; + + if( bSynchronous ) + ImplPrintJob(i_xController, i_rInitSetup); + else + { + PrintJobAsync* pAsync = new PrintJobAsync(i_xController, i_rInitSetup); + Application::PostUserEvent( LINK( pAsync, PrintJobAsync, ExecJob ) ); + } +} + +bool Printer::PreparePrintJob(std::shared_ptr<PrinterController> xController, + const JobSetup& i_rInitSetup) +{ + // check if there is a default printer; if not, show an error box (if appropriate) + if( GetDefaultPrinterName().isEmpty() ) + { + if (xController->isShowDialogs()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(xController->getWindow(), "vcl/ui/errornoprinterdialog.ui")); + std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorNoPrinterDialog")); + xBox->run(); + } + xController->setValue( "IsDirect", + css::uno::Any( false ) ); + } + + // setup printer + + // #i114306# changed behavior back from persistence + // if no specific printer is already set, create the default printer + if (!xController->getPrinter()) + { + OUString aPrinterName( i_rInitSetup.GetPrinterName() ); + VclPtrInstance<Printer> xPrinter( aPrinterName ); + xPrinter->SetJobSetup(i_rInitSetup); + xController->setPrinter(xPrinter); + xController->setPapersizeFromSetup(xPrinter->GetPrinterSettingsPreferred()); + } + + // reset last page property + xController->setLastPage(false); + + // update "PageRange" property inferring from other properties: + // case 1: "Pages" set from UNO API -> + // setup "Print Selection" and insert "PageRange" attribute + // case 2: "All pages" is selected + // update "Page range" attribute to have a sensible default, + // but leave "All" as selected + + // "Pages" attribute from API is now equivalent to "PageRange" + // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1 + // Argh ! That sure needs cleaning up + css::beans::PropertyValue* pContentVal = xController->getValue("PrintRange"); + if( ! pContentVal ) + pContentVal = xController->getValue("PrintContent"); + + // case 1: UNO API has set "Pages" + css::beans::PropertyValue* pPagesVal = xController->getValue("Pages"); + if( pPagesVal ) + { + OUString aPagesVal; + pPagesVal->Value >>= aPagesVal; + if( !aPagesVal.isEmpty() ) + { + // "Pages" attribute from API is now equivalent to "PageRange" + // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1 + // Argh ! That sure needs cleaning up + if( pContentVal ) + { + pContentVal->Value <<= sal_Int32( 1 ); + xController->setValue("PageRange", pPagesVal->Value); + } + } + } + // case 2: is "All" selected ? + else if( pContentVal ) + { + sal_Int32 nContent = -1; + if( pContentVal->Value >>= nContent ) + { + if( nContent == 0 ) + { + // do not overwrite PageRange if it is already set + css::beans::PropertyValue* pRangeVal = xController->getValue("PageRange"); + OUString aRange; + if( pRangeVal ) + pRangeVal->Value >>= aRange; + if( aRange.isEmpty() ) + { + sal_Int32 nPages = xController->getPageCount(); + if( nPages > 0 ) + { + OUStringBuffer aBuf( 32 ); + aBuf.append( "1" ); + if( nPages > 1 ) + { + aBuf.append( "-" ); + aBuf.append( nPages ); + } + xController->setValue("PageRange", css::uno::Any(aBuf.makeStringAndClear())); + } + } + } + } + } + + css::beans::PropertyValue* pReverseVal = xController->getValue("PrintReverse"); + if( pReverseVal ) + { + bool bReverse = false; + pReverseVal->Value >>= bReverse; + xController->setReversePrint( bReverse ); + } + + css::beans::PropertyValue* pPapersizeFromSetupVal = xController->getValue("PapersizeFromSetup"); + if( pPapersizeFromSetupVal ) + { + bool bPapersizeFromSetup = false; + pPapersizeFromSetupVal->Value >>= bPapersizeFromSetup; + xController->setPapersizeFromSetup(bPapersizeFromSetup); + } + + // setup NUp printing from properties + sal_Int32 nRows = xController->getIntProperty("NUpRows", 1); + sal_Int32 nCols = xController->getIntProperty("NUpColumns", 1); + if( nRows > 1 || nCols > 1 ) + { + PrinterController::MultiPageSetup aMPS; + aMPS.nRows = std::max<sal_Int32>(nRows, 1); + aMPS.nColumns = std::max<sal_Int32>(nCols, 1); + sal_Int32 nValue = xController->getIntProperty("NUpPageMarginLeft", aMPS.nLeftMargin); + if( nValue >= 0 ) + aMPS.nLeftMargin = nValue; + nValue = xController->getIntProperty("NUpPageMarginRight", aMPS.nRightMargin); + if( nValue >= 0 ) + aMPS.nRightMargin = nValue; + nValue = xController->getIntProperty( "NUpPageMarginTop", aMPS.nTopMargin ); + if( nValue >= 0 ) + aMPS.nTopMargin = nValue; + nValue = xController->getIntProperty( "NUpPageMarginBottom", aMPS.nBottomMargin ); + if( nValue >= 0 ) + aMPS.nBottomMargin = nValue; + nValue = xController->getIntProperty( "NUpHorizontalSpacing", aMPS.nHorizontalSpacing ); + if( nValue >= 0 ) + aMPS.nHorizontalSpacing = nValue; + nValue = xController->getIntProperty( "NUpVerticalSpacing", aMPS.nVerticalSpacing ); + if( nValue >= 0 ) + aMPS.nVerticalSpacing = nValue; + aMPS.bDrawBorder = xController->getBoolProperty( "NUpDrawBorder", aMPS.bDrawBorder ); + aMPS.nOrder = static_cast<NupOrderType>(xController->getIntProperty( "NUpSubPageOrder", static_cast<sal_Int32>(aMPS.nOrder) )); + aMPS.aPaperSize = xController->getPrinter()->PixelToLogic( xController->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ); + css::beans::PropertyValue* pPgSizeVal = xController->getValue( "NUpPaperSize" ); + css::awt::Size aSizeVal; + if( pPgSizeVal && (pPgSizeVal->Value >>= aSizeVal) ) + { + aMPS.aPaperSize.setWidth( aSizeVal.Width ); + aMPS.aPaperSize.setHeight( aSizeVal.Height ); + } + + xController->setMultipage( aMPS ); + } + + // in direct print case check whether there is anything to print. + // if not, show an errorbox (if appropriate) + if( xController->isShowDialogs() && xController->isDirectPrint() ) + { + if( xController->getFilteredPageCount() == 0 ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(xController->getWindow(), "vcl/ui/errornocontentdialog.ui")); + std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorNoContentDialog")); + xBox->run(); + return false; + } + } + + // check if the printer brings up its own dialog + // in that case leave the work to that dialog + if( ! xController->getPrinter()->GetCapabilities( PrinterCapType::ExternalDialog ) && + ! xController->isDirectPrint() && + xController->isShowDialogs() + ) + { + try + { + PrintDialog aDlg(xController->getWindow(), xController); + if (!aDlg.run()) + { + xController->abortJob(); + return false; + } + if (aDlg.isPrintToFile()) + { + OUString aFile = queryFile( xController->getPrinter().get() ); + if( aFile.isEmpty() ) + { + xController->abortJob(); + return false; + } + xController->setValue( "LocalFileName", + css::uno::Any( aFile ) ); + } + else if (aDlg.isSingleJobs()) + { + xController->getPrinter()->SetSinglePrintJobs(true); + } + } + catch (const std::bad_alloc&) + { + } + } + + xController->pushPropertiesToPrinter(); + return true; +} + +bool Printer::ExecutePrintJob(const std::shared_ptr<PrinterController>& xController) +{ + OUString aJobName; + css::beans::PropertyValue* pJobNameVal = xController->getValue( "JobName" ); + if( pJobNameVal ) + pJobNameVal->Value >>= aJobName; + + return xController->getPrinter()->StartJob( aJobName, xController ); +} + +void Printer::FinishPrintJob(const std::shared_ptr<PrinterController>& xController) +{ + xController->resetPaperToLastConfigured(); + xController->jobFinished( xController->getJobState() ); +} + +void Printer::ImplPrintJob(const std::shared_ptr<PrinterController>& xController, + const JobSetup& i_rInitSetup) +{ + if (PreparePrintJob(xController, i_rInitSetup)) + { + ExecutePrintJob(xController); + } + FinishPrintJob(xController); +} + +bool Printer::StartJob( const OUString& i_rJobName, std::shared_ptr<vcl::PrinterController> const & i_xController) +{ + mnError = ERRCODE_NONE; + + if ( IsDisplayPrinter() ) + return false; + + if ( IsJobActive() || IsPrinting() ) + return false; + + sal_uInt32 nCopies = mnCopyCount; + bool bCollateCopy = mbCollateCopy; + bool bUserCopy = false; + + if ( nCopies > 1 ) + { + const sal_uInt32 nDevCopy = GetCapabilities( bCollateCopy + ? PrinterCapType::CollateCopies + : PrinterCapType::Copies ); + + // need to do copies by hand ? + if ( nCopies > nDevCopy ) + { + bUserCopy = true; + nCopies = 1; + bCollateCopy = false; + } + } + else + bCollateCopy = false; + + ImplSVData* pSVData = ImplGetSVData(); + mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter ); + + if (!mpPrinter) + return false; + + bool bSinglePrintJobs = i_xController->getPrinter()->IsSinglePrintJobs(); + + css::beans::PropertyValue* pFileValue = i_xController->getValue("LocalFileName"); + if( pFileValue ) + { + OUString aFile; + pFileValue->Value >>= aFile; + if( !aFile.isEmpty() ) + { + mbPrintFile = true; + maPrintFile = aFile; + bSinglePrintJobs = false; + } + } + + OUString* pPrintFile = nullptr; + if ( mbPrintFile ) + pPrintFile = &maPrintFile; + mpPrinterOptions->ReadFromConfig( mbPrintFile ); + + mbPrinting = true; + if( GetCapabilities( PrinterCapType::UsePullModel ) ) + { + mbJobActive = true; + // SAL layer does all necessary page printing + // and also handles showing a dialog + // that also means it must call jobStarted when the dialog is finished + // it also must set the JobState of the Controller + if( mpPrinter->StartJob( pPrintFile, + i_rJobName, + Application::GetDisplayName(), + &maJobSetup.ImplGetData(), + *i_xController) ) + { + EndJob(); + } + else + { + mnError = ImplSalPrinterErrorCodeToVCL(mpPrinter->GetErrorCode()); + if ( !mnError ) + mnError = PRINTER_GENERALERROR; + mbPrinting = false; + mpPrinter.reset(); + mbJobActive = false; + + GDIMetaFile aDummyFile; + i_xController->setLastPage(true); + i_xController->getFilteredPageFile(0, aDummyFile); + + return false; + } + } + else + { + // possibly a dialog has been shown + // now the real job starts + i_xController->setJobState( css::view::PrintableState_JOB_STARTED ); + i_xController->jobStarted(); + + int nJobs = 1; + int nOuterRepeatCount = 1; + int nInnerRepeatCount = 1; + if( bUserCopy ) + { + if( mbCollateCopy ) + nOuterRepeatCount = mnCopyCount; + else + nInnerRepeatCount = mnCopyCount; + } + if( bSinglePrintJobs ) + { + nJobs = mnCopyCount; + nCopies = 1; + nOuterRepeatCount = nInnerRepeatCount = 1; + } + + for( int nJobIteration = 0; nJobIteration < nJobs; nJobIteration++ ) + { + bool bError = false; + if( mpPrinter->StartJob( pPrintFile, + i_rJobName, + Application::GetDisplayName(), + nCopies, + bCollateCopy, + i_xController->isDirectPrint(), + &maJobSetup.ImplGetData() ) ) + { + bool bAborted = false; + mbJobActive = true; + i_xController->createProgressDialog(); + const int nPages = i_xController->getFilteredPageCount(); + // abort job, if no pages will be printed. + if ( nPages == 0 ) + { + i_xController->abortJob(); + bAborted = true; + } + for( int nOuterIteration = 0; nOuterIteration < nOuterRepeatCount && ! bAborted; nOuterIteration++ ) + { + for( int nPage = 0; nPage < nPages && ! bAborted; nPage++ ) + { + for( int nInnerIteration = 0; nInnerIteration < nInnerRepeatCount && ! bAborted; nInnerIteration++ ) + { + if( nPage == nPages-1 && + nOuterIteration == nOuterRepeatCount-1 && + nInnerIteration == nInnerRepeatCount-1 && + nJobIteration == nJobs-1 ) + { + i_xController->setLastPage(true); + } + i_xController->printFilteredPage(nPage); + if (i_xController->isProgressCanceled()) + { + i_xController->abortJob(); + } + if (i_xController->getJobState() == + css::view::PrintableState_JOB_ABORTED) + { + bAborted = true; + } + } + } + // FIXME: duplex ? + } + EndJob(); + + if( nJobIteration < nJobs-1 ) + { + mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter ); + + if ( mpPrinter ) + mbPrinting = true; + else + bError = true; + } + } + else + bError = true; + + if( bError ) + { + mnError = mpPrinter ? ImplSalPrinterErrorCodeToVCL(mpPrinter->GetErrorCode()) : ERRCODE_NONE; + if ( !mnError ) + mnError = PRINTER_GENERALERROR; + i_xController->setJobState( mnError == PRINTER_ABORT + ? css::view::PrintableState_JOB_ABORTED + : css::view::PrintableState_JOB_FAILED ); + mbPrinting = false; + mpPrinter.reset(); + + return false; + } + } + + if (i_xController->getJobState() == css::view::PrintableState_JOB_STARTED) + i_xController->setJobState(css::view::PrintableState_JOB_SPOOLED); + } + + // make last used printer persistent for UI jobs + if (i_xController->isShowDialogs() && !i_xController->isDirectPrint()) + { + SettingsConfigItem* pItem = SettingsConfigItem::get(); + pItem->setValue( "PrintDialog", + "LastPrinterUsed", + GetName() + ); + } + + return true; +} + +PrinterController::~PrinterController() +{ +} + +css::view::PrintableState PrinterController::getJobState() const +{ + return mpImplData->meJobState; +} + +void PrinterController::setJobState( css::view::PrintableState i_eState ) +{ + mpImplData->meJobState = i_eState; +} + +const VclPtr<Printer>& PrinterController::getPrinter() const +{ + return mpImplData->mxPrinter; +} + +weld::Window* PrinterController::getWindow() const +{ + return mpImplData->mpWindow; +} + +void PrinterController::dialogsParentClosing() +{ + mpImplData->mpWindow = nullptr; + if (mpImplData->mxProgress) + { + // close the dialog without doing anything, just get rid of it + mpImplData->mxProgress->response(RET_OK); + mpImplData->mxProgress.reset(); + } +} + +void PrinterController::setPrinter( const VclPtr<Printer>& i_rPrinter ) +{ + VclPtr<Printer> xPrinter = mpImplData->mxPrinter; + + Size aPaperSize; // Save current paper size + Orientation eOrientation = Orientation::Portrait; // Save current paper orientation + bool bSavedSizeOrientation = false; + + // #tdf 126744 Transfer paper size and orientation settings to newly selected printer + if ( xPrinter ) + { + aPaperSize = xPrinter->GetPaperSize(); + eOrientation = xPrinter->GetOrientation(); + bSavedSizeOrientation = true; + } + + mpImplData->mxPrinter = i_rPrinter; + setValue( "Name", + css::uno::Any( i_rPrinter->GetName() ) ); + mpImplData->mnDefaultPaperBin = mpImplData->mxPrinter->GetPaperBin(); + mpImplData->mxPrinter->Push(); + mpImplData->mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); + mpImplData->maDefaultPageSize = mpImplData->mxPrinter->GetPaperSize(); + + if ( bSavedSizeOrientation ) + { + mpImplData->mxPrinter->SetPaperSizeUser(aPaperSize); + mpImplData->mxPrinter->SetOrientation(eOrientation); + } + + mpImplData->mbPapersizeFromUser = false; + mpImplData->mbOrientationFromUser = false; + mpImplData->mxPrinter->Pop(); + mpImplData->mnFixedPaperBin = -1; +} + +void PrinterController::resetPrinterOptions( bool i_bFileOutput ) +{ + vcl::printer::Options aOpt; + aOpt.ReadFromConfig( i_bFileOutput ); + mpImplData->mxPrinter->SetPrinterOptions( aOpt ); +} + +void PrinterController::setupPrinter( weld::Window* i_pParent ) +{ + bool bRet = false; + + // Important to hold printer alive while doing setup etc. + VclPtr< Printer > xPrinter = mpImplData->mxPrinter; + + if( !xPrinter ) + return; + + xPrinter->Push(); + xPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); + + // get current data + Size aPaperSize(xPrinter->GetPaperSize()); + Orientation eOrientation = xPrinter->GetOrientation(); + sal_uInt16 nPaperBin = xPrinter->GetPaperBin(); + + // reset paper size back to last configured size, not + // whatever happens to be the current page + // (but only if the printer config has changed, otherwise + // don't override printer page auto-detection - tdf#91362) + if (getPrinterModified() || getPapersizeFromSetup()) + { + resetPaperToLastConfigured(); + } + + // call driver setup + bRet = xPrinter->Setup( i_pParent, PrinterSetupMode::SingleJob ); + SAL_WARN_IF(xPrinter != mpImplData->mxPrinter, "vcl.gdi", + "Printer changed underneath us during setup"); + xPrinter = mpImplData->mxPrinter; + + Size aNewPaperSize(xPrinter->GetPaperSize()); + if (bRet) + { + bool bInvalidateCache = false; + setPapersizeFromSetup(xPrinter->GetPrinterSettingsPreferred()); + + // was papersize overridden ? if so we need to take action if we're + // configured to use the driver papersize + if (aNewPaperSize != mpImplData->maDefaultPageSize) + { + mpImplData->maDefaultPageSize = aNewPaperSize; + bInvalidateCache = getPapersizeFromSetup(); + } + + // was bin overridden ? if so we need to take action + sal_uInt16 nNewPaperBin = xPrinter->GetPaperBin(); + if (nNewPaperBin != nPaperBin) + { + mpImplData->mnFixedPaperBin = nNewPaperBin; + bInvalidateCache = true; + } + + if (bInvalidateCache) + { + mpImplData->maPageCache.invalidate(); + } + } + else + { + //restore to whatever it was before we entered this method + xPrinter->SetOrientation( eOrientation ); + if (aPaperSize != aNewPaperSize) + xPrinter->SetPaperSizeUser(aPaperSize); + } + xPrinter->Pop(); +} + +PrinterController::PageSize vcl::ImplPrinterControllerData::modifyJobSetup( const css::uno::Sequence< css::beans::PropertyValue >& i_rProps ) +{ + PrinterController::PageSize aPageSize; + aPageSize.aSize = mxPrinter->GetPaperSize(); + css::awt::Size aSetSize, aIsSize; + sal_Int32 nPaperBin = mnDefaultPaperBin; + for( const auto& rProp : i_rProps ) + { + if ( rProp.Name == "PreferredPageSize" ) + { + rProp.Value >>= aSetSize; + } + else if ( rProp.Name == "PageSize" ) + { + rProp.Value >>= aIsSize; + } + else if ( rProp.Name == "PageIncludesNonprintableArea" ) + { + bool bVal = false; + rProp.Value >>= bVal; + aPageSize.bFullPaper = bVal; + } + else if ( rProp.Name == "PrinterPaperTray" ) + { + sal_Int32 nBin = -1; + rProp.Value >>= nBin; + if( nBin >= 0 && o3tl::make_unsigned(nBin) < mxPrinter->GetPaperBinCount() ) + nPaperBin = nBin; + } + } + + Size aCurSize( mxPrinter->GetPaperSize() ); + if( aSetSize.Width && aSetSize.Height ) + { + Size aSetPaperSize( aSetSize.Width, aSetSize.Height ); + Size aRealPaperSize( getRealPaperSize( aSetPaperSize, true/*bNoNUP*/ ) ); + if( aRealPaperSize != aCurSize ) + aIsSize = aSetSize; + } + + if( aIsSize.Width && aIsSize.Height ) + { + aPageSize.aSize.setWidth( aIsSize.Width ); + aPageSize.aSize.setHeight( aIsSize.Height ); + + Size aRealPaperSize( getRealPaperSize( aPageSize.aSize, true/*bNoNUP*/ ) ); + if( aRealPaperSize != aCurSize ) + mxPrinter->SetPaperSizeUser( aRealPaperSize ); + } + + // paper bin set from properties in print dialog overrides + // application default for a page + if ( mnFixedPaperBin != -1 ) + nPaperBin = mnFixedPaperBin; + + if( nPaperBin != -1 && nPaperBin != mxPrinter->GetPaperBin() ) + mxPrinter->SetPaperBin( nPaperBin ); + + return aPageSize; +} + +//fdo#61886 + +//when printing is finished, set the paper size of the printer to either what +//the user explicitly set as the desired paper size, or fallback to whatever +//the printer had before printing started. That way it doesn't contain the last +//paper size of a multiple paper size using document when we are in our normal +//auto accept document paper size mode and end up overwriting the original +//paper size setting for file->printer_settings just by pressing "ok" in the +//print dialog +void vcl::ImplPrinterControllerData::resetPaperToLastConfigured() +{ + mxPrinter->Push(); + mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); + Size aCurSize(mxPrinter->GetPaperSize()); + if (aCurSize != maDefaultPageSize) + mxPrinter->SetPaperSizeUser(maDefaultPageSize); + mxPrinter->Pop(); +} + +int PrinterController::getPageCountProtected() const +{ + const MapMode aMapMode( MapUnit::Map100thMM ); + + mpImplData->mxPrinter->Push(); + mpImplData->mxPrinter->SetMapMode( aMapMode ); + int nPages = getPageCount(); + mpImplData->mxPrinter->Pop(); + return nPages; +} + +css::uno::Sequence< css::beans::PropertyValue > PrinterController::getPageParametersProtected( int i_nPage ) const +{ + const MapMode aMapMode( MapUnit::Map100thMM ); + + mpImplData->mxPrinter->Push(); + mpImplData->mxPrinter->SetMapMode( aMapMode ); + css::uno::Sequence< css::beans::PropertyValue > aResult( getPageParameters( i_nPage ) ); + mpImplData->mxPrinter->Pop(); + return aResult; +} + +PrinterController::PageSize PrinterController::getPageFile( int i_nUnfilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache ) +{ + // update progress if necessary + if( mpImplData->mxProgress ) + { + // do nothing if printing is canceled + if( mpImplData->mxProgress->isCanceled() ) + return PrinterController::PageSize(); + mpImplData->mxProgress->tick(); + Application::Reschedule( true ); + } + + if( i_bMayUseCache ) + { + PrinterController::PageSize aPageSize; + if( mpImplData->maPageCache.get( i_nUnfilteredPage, o_rMtf, aPageSize ) ) + { + return aPageSize; + } + } + else + mpImplData->maPageCache.invalidate(); + + o_rMtf.Clear(); + + // get page parameters + css::uno::Sequence< css::beans::PropertyValue > aPageParm( getPageParametersProtected( i_nUnfilteredPage ) ); + const MapMode aMapMode( MapUnit::Map100thMM ); + + mpImplData->mxPrinter->Push(); + mpImplData->mxPrinter->SetMapMode( aMapMode ); + + // modify job setup if necessary + PrinterController::PageSize aPageSize = mpImplData->modifyJobSetup( aPageParm ); + + o_rMtf.SetPrefSize( aPageSize.aSize ); + o_rMtf.SetPrefMapMode( aMapMode ); + + mpImplData->mxPrinter->EnableOutput( false ); + + o_rMtf.Record( mpImplData->mxPrinter.get() ); + + printPage( i_nUnfilteredPage ); + + o_rMtf.Stop(); + o_rMtf.WindStart(); + mpImplData->mxPrinter->Pop(); + + if( i_bMayUseCache ) + mpImplData->maPageCache.insert( i_nUnfilteredPage, o_rMtf, aPageSize ); + + // reset "FirstPage" property to false now we've gotten at least our first one + mpImplData->mbFirstPage = false; + + return aPageSize; +} + +static void appendSubPage( GDIMetaFile& o_rMtf, const tools::Rectangle& i_rClipRect, GDIMetaFile& io_rSubPage, bool i_bDrawBorder ) +{ + // intersect all clipregion actions with our clip rect + io_rSubPage.WindStart(); + io_rSubPage.Clip( i_rClipRect ); + + // save gstate + o_rMtf.AddAction( new MetaPushAction( PushFlags::ALL ) ); + + // clip to page rect + o_rMtf.AddAction( new MetaClipRegionAction( vcl::Region( i_rClipRect ), true ) ); + + // append the subpage + io_rSubPage.WindStart(); + io_rSubPage.Play( o_rMtf ); + + // restore gstate + o_rMtf.AddAction( new MetaPopAction() ); + + // draw a border + if( !i_bDrawBorder ) + return; + + // save gstate + o_rMtf.AddAction( new MetaPushAction( PushFlags::LINECOLOR | PushFlags::FILLCOLOR | PushFlags::CLIPREGION | PushFlags::MAPMODE ) ); + o_rMtf.AddAction( new MetaMapModeAction( MapMode( MapUnit::Map100thMM ) ) ); + + tools::Rectangle aBorderRect( i_rClipRect ); + o_rMtf.AddAction( new MetaLineColorAction( COL_BLACK, true ) ); + o_rMtf.AddAction( new MetaFillColorAction( COL_TRANSPARENT, false ) ); + o_rMtf.AddAction( new MetaRectAction( aBorderRect ) ); + + // restore gstate + o_rMtf.AddAction( new MetaPopAction() ); +} + +PrinterController::PageSize PrinterController::getFilteredPageFile( int i_nFilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache ) +{ + const MultiPageSetup& rMPS( mpImplData->maMultiPage ); + int nSubPages = rMPS.nRows * rMPS.nColumns; + if( nSubPages < 1 ) + nSubPages = 1; + + // reverse sheet order + if( mpImplData->mbReversePageOrder ) + { + int nDocPages = getFilteredPageCount(); + i_nFilteredPage = nDocPages - 1 - i_nFilteredPage; + } + + // there is no filtering to be done (and possibly the page size of the + // original page is to be set), when N-Up is "neutral" that is there is + // only one subpage and the margins are 0 + if( nSubPages == 1 && + rMPS.nLeftMargin == 0 && rMPS.nRightMargin == 0 && + rMPS.nTopMargin == 0 && rMPS.nBottomMargin == 0 ) + { + PrinterController::PageSize aPageSize = getPageFile( i_nFilteredPage, o_rMtf, i_bMayUseCache ); + if (mpImplData->meJobState != css::view::PrintableState_JOB_STARTED) + { // rhbz#657394: check that we are still printing... + return PrinterController::PageSize(); + } + Size aPaperSize = mpImplData->getRealPaperSize( aPageSize.aSize, true ); + mpImplData->mxPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) ); + mpImplData->mxPrinter->SetPaperSizeUser( aPaperSize ); + if( aPaperSize != aPageSize.aSize ) + { + // user overridden page size, center Metafile + o_rMtf.WindStart(); + tools::Long nDX = (aPaperSize.Width() - aPageSize.aSize.Width()) / 2; + tools::Long nDY = (aPaperSize.Height() - aPageSize.aSize.Height()) / 2; + o_rMtf.Move( nDX, nDY, mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() ); + o_rMtf.WindStart(); + o_rMtf.SetPrefSize( aPaperSize ); + aPageSize.aSize = aPaperSize; + } + return aPageSize; + } + + // set last page property really only on the very last page to be rendered + // that is on the last subpage of a NUp run + bool bIsLastPage = mpImplData->mbLastPage; + mpImplData->mbLastPage = false; + + Size aPaperSize( mpImplData->getRealPaperSize( mpImplData->maMultiPage.aPaperSize, false ) ); + + // multi page area: page size minus margins + one time spacing right and down + // the added spacing is so each subpage can be calculated including its spacing + Size aMPArea( aPaperSize ); + aMPArea.AdjustWidth( -(rMPS.nLeftMargin + rMPS.nRightMargin) ); + aMPArea.AdjustWidth(rMPS.nHorizontalSpacing ); + aMPArea.AdjustHeight( -(rMPS.nTopMargin + rMPS.nBottomMargin) ); + aMPArea.AdjustHeight(rMPS.nVerticalSpacing ); + + // determine offsets + tools::Long nAdvX = aMPArea.Width() / rMPS.nColumns; + tools::Long nAdvY = aMPArea.Height() / rMPS.nRows; + + // determine size of a "cell" subpage, leave a little space around pages + Size aSubPageSize( nAdvX - rMPS.nHorizontalSpacing, nAdvY - rMPS.nVerticalSpacing ); + + o_rMtf.Clear(); + o_rMtf.SetPrefSize( aPaperSize ); + o_rMtf.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) ); + o_rMtf.AddAction( new MetaMapModeAction( MapMode( MapUnit::Map100thMM ) ) ); + + int nDocPages = getPageCountProtected(); + if (mpImplData->meJobState != css::view::PrintableState_JOB_STARTED) + { // rhbz#657394: check that we are still printing... + return PrinterController::PageSize(); + } + for( int nSubPage = 0; nSubPage < nSubPages; nSubPage++ ) + { + // map current sub page to real page + int nPage = i_nFilteredPage * nSubPages + nSubPage; + if( nSubPage == nSubPages-1 || + nPage == nDocPages-1 ) + { + mpImplData->mbLastPage = bIsLastPage; + } + if( nPage >= 0 && nPage < nDocPages ) + { + GDIMetaFile aPageFile; + PrinterController::PageSize aPageSize = getPageFile( nPage, aPageFile, i_bMayUseCache ); + if( aPageSize.aSize.Width() && aPageSize.aSize.Height() ) + { + tools::Long nCellX = 0, nCellY = 0; + switch( rMPS.nOrder ) + { + case NupOrderType::LRTB: + nCellX = (nSubPage % rMPS.nColumns); + nCellY = (nSubPage / rMPS.nColumns); + break; + case NupOrderType::TBLR: + nCellX = (nSubPage / rMPS.nRows); + nCellY = (nSubPage % rMPS.nRows); + break; + case NupOrderType::RLTB: + nCellX = rMPS.nColumns - 1 - (nSubPage % rMPS.nColumns); + nCellY = (nSubPage / rMPS.nColumns); + break; + case NupOrderType::TBRL: + nCellX = rMPS.nColumns - 1 - (nSubPage / rMPS.nRows); + nCellY = (nSubPage % rMPS.nRows); + break; + } + // scale the metafile down to a sub page size + double fScaleX = double(aSubPageSize.Width())/double(aPageSize.aSize.Width()); + double fScaleY = double(aSubPageSize.Height())/double(aPageSize.aSize.Height()); + double fScale = std::min( fScaleX, fScaleY ); + aPageFile.Scale( fScale, fScale ); + aPageFile.WindStart(); + + // move the subpage so it is centered in its "cell" + tools::Long nOffX = (aSubPageSize.Width() - tools::Long(double(aPageSize.aSize.Width()) * fScale)) / 2; + tools::Long nOffY = (aSubPageSize.Height() - tools::Long(double(aPageSize.aSize.Height()) * fScale)) / 2; + tools::Long nX = rMPS.nLeftMargin + nOffX + nAdvX * nCellX; + tools::Long nY = rMPS.nTopMargin + nOffY + nAdvY * nCellY; + aPageFile.Move( nX, nY, mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() ); + aPageFile.WindStart(); + // calculate border rectangle + tools::Rectangle aSubPageRect( Point( nX, nY ), + Size( tools::Long(double(aPageSize.aSize.Width())*fScale), + tools::Long(double(aPageSize.aSize.Height())*fScale) ) ); + + // append subpage to page + appendSubPage( o_rMtf, aSubPageRect, aPageFile, rMPS.bDrawBorder ); + } + } + } + o_rMtf.WindStart(); + + // subsequent getPageFile calls have changed the paper, reset it to current value + mpImplData->mxPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) ); + mpImplData->mxPrinter->SetPaperSizeUser( aPaperSize ); + + return PrinterController::PageSize( aPaperSize, true ); +} + +int PrinterController::getFilteredPageCount() const +{ + int nDiv = mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns; + if( nDiv < 1 ) + nDiv = 1; + return (getPageCountProtected() + (nDiv-1)) / nDiv; +} + +DrawModeFlags PrinterController::removeTransparencies( GDIMetaFile const & i_rIn, GDIMetaFile& o_rOut ) +{ + DrawModeFlags nRestoreDrawMode = mpImplData->mxPrinter->GetDrawMode(); + sal_Int32 nMaxBmpDPIX = mpImplData->mxPrinter->GetDPIX(); + sal_Int32 nMaxBmpDPIY = mpImplData->mxPrinter->GetDPIY(); + + const vcl::printer::Options& rPrinterOptions = mpImplData->mxPrinter->GetPrinterOptions(); + + static const sal_Int32 OPTIMAL_BMP_RESOLUTION = 300; + static const sal_Int32 NORMAL_BMP_RESOLUTION = 200; + + if( rPrinterOptions.IsReduceBitmaps() ) + { + // calculate maximum resolution for bitmap graphics + if( printer::BitmapMode::Optimal == rPrinterOptions.GetReducedBitmapMode() ) + { + nMaxBmpDPIX = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIX ); + nMaxBmpDPIY = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIY ); + } + else if( printer::BitmapMode::Normal == rPrinterOptions.GetReducedBitmapMode() ) + { + nMaxBmpDPIX = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIX ); + nMaxBmpDPIY = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIY ); + } + else + { + nMaxBmpDPIX = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIX ); + nMaxBmpDPIY = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIY ); + } + } + + // convert to greyscales + if( rPrinterOptions.IsConvertToGreyscales() ) + { + mpImplData->mxPrinter->SetDrawMode( mpImplData->mxPrinter->GetDrawMode() | + ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText | + DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) ); + } + + // disable transparency output + if( rPrinterOptions.IsReduceTransparency() && ( vcl::printer::TransparencyMode::NONE == rPrinterOptions.GetReducedTransparencyMode() ) ) + { + mpImplData->mxPrinter->SetDrawMode( mpImplData->mxPrinter->GetDrawMode() | DrawModeFlags::NoTransparency ); + } + + Color aBg( COL_TRANSPARENT ); // default: let RemoveTransparenciesFromMetaFile do its own background logic + if( mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns > 1 ) + { + // in N-Up printing we have no "page" background operation + // we also have no way to determine the paper color + // so let's go for white, which will kill 99.9% of the real cases + aBg = COL_WHITE; + } + mpImplData->mxPrinter->RemoveTransparenciesFromMetaFile( i_rIn, o_rOut, nMaxBmpDPIX, nMaxBmpDPIY, + rPrinterOptions.IsReduceTransparency(), + rPrinterOptions.GetReducedTransparencyMode() == vcl::printer::TransparencyMode::Auto, + rPrinterOptions.IsReduceBitmaps() && rPrinterOptions.IsReducedBitmapIncludesTransparency(), + aBg + ); + return nRestoreDrawMode; +} + +void PrinterController::printFilteredPage( int i_nPage ) +{ + if( mpImplData->meJobState != css::view::PrintableState_JOB_STARTED ) + return; // rhbz#657394: check that we are still printing... + + GDIMetaFile aPageFile; + PrinterController::PageSize aPageSize = getFilteredPageFile( i_nPage, aPageFile ); + + if( mpImplData->mxProgress ) + { + // do nothing if printing is canceled + if( mpImplData->mxProgress->isCanceled() ) + { + setJobState( css::view::PrintableState_JOB_ABORTED ); + return; + } + } + + // in N-Up printing set the correct page size + mpImplData->mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); + // aPageSize was filtered through mpImplData->getRealPaperSize already by getFilteredPageFile() + mpImplData->mxPrinter->SetPaperSizeUser( aPageSize.aSize ); + if( mpImplData->mnFixedPaperBin != -1 && + mpImplData->mxPrinter->GetPaperBin() != mpImplData->mnFixedPaperBin ) + { + mpImplData->mxPrinter->SetPaperBin( mpImplData->mnFixedPaperBin ); + } + + // if full paper is meant to be used, move the output to accommodate for pageoffset + if( aPageSize.bFullPaper ) + { + Point aPageOffset( mpImplData->mxPrinter->GetPageOffset() ); + aPageFile.WindStart(); + aPageFile.Move( -aPageOffset.X(), -aPageOffset.Y(), mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() ); + } + + GDIMetaFile aCleanedFile; + DrawModeFlags nRestoreDrawMode = removeTransparencies( aPageFile, aCleanedFile ); + + mpImplData->mxPrinter->EnableOutput(); + + // actually print the page + mpImplData->mxPrinter->ImplStartPage(); + + mpImplData->mxPrinter->Push(); + aCleanedFile.WindStart(); + aCleanedFile.Play(*mpImplData->mxPrinter); + mpImplData->mxPrinter->Pop(); + + mpImplData->mxPrinter->ImplEndPage(); + + mpImplData->mxPrinter->SetDrawMode( nRestoreDrawMode ); +} + +void PrinterController::jobStarted() +{ +} + +void PrinterController::jobFinished( css::view::PrintableState ) +{ +} + +void PrinterController::abortJob() +{ + setJobState( css::view::PrintableState_JOB_ABORTED ); + // applications (well, sw) depend on a page request with "IsLastPage" = true + // to free resources, else they (well, sw) will crash eventually + setLastPage( true ); + + if (mpImplData->mxProgress) + { + mpImplData->mxProgress->response(RET_CANCEL); + mpImplData->mxProgress.reset(); + } + + GDIMetaFile aMtf; + getPageFile( 0, aMtf ); +} + +void PrinterController::setLastPage( bool i_bLastPage ) +{ + mpImplData->mbLastPage = i_bLastPage; +} + +void PrinterController::setReversePrint( bool i_bReverse ) +{ + mpImplData->mbReversePageOrder = i_bReverse; +} + +void PrinterController::setPapersizeFromSetup( bool i_bPapersizeFromSetup ) +{ + mpImplData->mbPapersizeFromSetup = i_bPapersizeFromSetup; + mpImplData->mxPrinter->SetPrinterSettingsPreferred( i_bPapersizeFromSetup ); + if ( i_bPapersizeFromSetup ) + { + mpImplData->mbPapersizeFromUser = false; + mpImplData->mbOrientationFromUser = false; + } +} + +bool PrinterController::getPapersizeFromSetup() const +{ + return mpImplData->mbPapersizeFromSetup; +} + +void PrinterController::setPaperSizeFromUser( Size i_aUserSize ) +{ + mpImplData->mbPapersizeFromUser = true; + mpImplData->mbPapersizeFromSetup = false; + mpImplData->mxPrinter->SetPrinterSettingsPreferred( false ); + + mpImplData->maUserPageSize = i_aUserSize; +} + +void PrinterController::setOrientationFromUser( Orientation eOrientation, bool set ) +{ + mpImplData->mbOrientationFromUser = set; + mpImplData->meUserOrientation = eOrientation; +} + +void PrinterController::setPrinterModified( bool i_bPrinterModified ) +{ + mpImplData->mbPrinterModified = i_bPrinterModified; +} + +bool PrinterController::getPrinterModified() const +{ + return mpImplData->mbPrinterModified; +} + +css::uno::Sequence< css::beans::PropertyValue > PrinterController::getJobProperties( const css::uno::Sequence< css::beans::PropertyValue >& i_rMergeList ) const +{ + std::unordered_set< OUString > aMergeSet; + size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3; + for( const auto& rPropVal : i_rMergeList ) + aMergeSet.insert( rPropVal.Name ); + + css::uno::Sequence< css::beans::PropertyValue > aResult( nResultLen ); + auto pResult = aResult.getArray(); + std::copy(i_rMergeList.begin(), i_rMergeList.end(), pResult); + int nCur = i_rMergeList.getLength(); + for(const css::beans::PropertyValue & rPropVal : mpImplData->maUIProperties) + { + if( aMergeSet.find( rPropVal.Name ) == aMergeSet.end() ) + pResult[nCur++] = rPropVal; + } + // append IsFirstPage + if( aMergeSet.find( "IsFirstPage" ) == aMergeSet.end() ) + { + css::beans::PropertyValue aVal; + aVal.Name = "IsFirstPage"; + aVal.Value <<= mpImplData->mbFirstPage; + pResult[nCur++] = aVal; + } + // append IsLastPage + if( aMergeSet.find( "IsLastPage" ) == aMergeSet.end() ) + { + css::beans::PropertyValue aVal; + aVal.Name = "IsLastPage"; + aVal.Value <<= mpImplData->mbLastPage; + pResult[nCur++] = aVal; + } + // append IsPrinter + if( aMergeSet.find( "IsPrinter" ) == aMergeSet.end() ) + { + css::beans::PropertyValue aVal; + aVal.Name = "IsPrinter"; + aVal.Value <<= true; + pResult[nCur++] = aVal; + } + aResult.realloc( nCur ); + return aResult; +} + +const css::uno::Sequence< css::beans::PropertyValue >& PrinterController::getUIOptions() const +{ + return mpImplData->maUIOptions; +} + +css::beans::PropertyValue* PrinterController::getValue( const OUString& i_rProperty ) +{ + std::unordered_map< OUString, size_t >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : nullptr; +} + +const css::beans::PropertyValue* PrinterController::getValue( const OUString& i_rProperty ) const +{ + std::unordered_map< OUString, size_t >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : nullptr; +} + +void PrinterController::setValue( const OUString& i_rPropertyName, const css::uno::Any& i_rValue ) +{ + css::beans::PropertyValue aVal; + aVal.Name = i_rPropertyName; + aVal.Value = i_rValue; + + setValue( aVal ); +} + +void PrinterController::setValue( const css::beans::PropertyValue& i_rPropertyValue ) +{ + std::unordered_map< OUString, size_t >::const_iterator it = + mpImplData->maPropertyToIndex.find( i_rPropertyValue.Name ); + if( it != mpImplData->maPropertyToIndex.end() ) + mpImplData->maUIProperties[ it->second ] = i_rPropertyValue; + else + { + // insert correct index into property map + mpImplData->maPropertyToIndex[ i_rPropertyValue.Name ] = mpImplData->maUIProperties.size(); + mpImplData->maUIProperties.push_back( i_rPropertyValue ); + mpImplData->maUIPropertyEnabled.push_back( true ); + } +} + +void PrinterController::setUIOptions( const css::uno::Sequence< css::beans::PropertyValue >& i_rOptions ) +{ + SAL_WARN_IF( mpImplData->maUIOptions.hasElements(), "vcl.gdi", "setUIOptions called twice !" ); + + mpImplData->maUIOptions = i_rOptions; + + for( const auto& rOpt : i_rOptions ) + { + css::uno::Sequence< css::beans::PropertyValue > aOptProp; + rOpt.Value >>= aOptProp; + bool bIsEnabled = true; + bool bHaveProperty = false; + OUString aPropName; + vcl::ImplPrinterControllerData::ControlDependency aDep; + css::uno::Sequence< sal_Bool > aChoicesDisabled; + for( const css::beans::PropertyValue& rEntry : std::as_const(aOptProp) ) + { + if ( rEntry.Name == "Property" ) + { + css::beans::PropertyValue aVal; + rEntry.Value >>= aVal; + DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name ) + == mpImplData->maPropertyToIndex.end(), "duplicate property entry" ); + setValue( aVal ); + aPropName = aVal.Name; + bHaveProperty = true; + } + else if ( rEntry.Name == "Enabled" ) + { + bool bValue = true; + rEntry.Value >>= bValue; + bIsEnabled = bValue; + } + else if ( rEntry.Name == "DependsOnName" ) + { + rEntry.Value >>= aDep.maDependsOnName; + } + else if ( rEntry.Name == "DependsOnEntry" ) + { + rEntry.Value >>= aDep.mnDependsOnEntry; + } + else if ( rEntry.Name == "ChoicesDisabled" ) + { + rEntry.Value >>= aChoicesDisabled; + } + } + if( bHaveProperty ) + { + vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it = + mpImplData->maPropertyToIndex.find( aPropName ); + // sanity check + if( it != mpImplData->maPropertyToIndex.end() ) + { + mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled; + } + if( !aDep.maDependsOnName.isEmpty() ) + mpImplData->maControlDependencies[ aPropName ] = aDep; + if( aChoicesDisabled.hasElements() ) + mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled; + } + } +} + +bool PrinterController::isUIOptionEnabled( const OUString& i_rProperty ) const +{ + bool bEnabled = false; + std::unordered_map< OUString, size_t >::const_iterator prop_it = + mpImplData->maPropertyToIndex.find( i_rProperty ); + if( prop_it != mpImplData->maPropertyToIndex.end() ) + { + bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second]; + + if( bEnabled ) + { + // check control dependencies + vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it = + mpImplData->maControlDependencies.find( i_rProperty ); + if( it != mpImplData->maControlDependencies.end() ) + { + // check if the dependency is enabled + // if the dependency is disabled, we are too + bEnabled = isUIOptionEnabled( it->second.maDependsOnName ); + + if( bEnabled ) + { + // does the dependency have the correct value ? + const css::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName ); + OSL_ENSURE( pVal, "unknown property in dependency" ); + if( pVal ) + { + sal_Int32 nDepVal = 0; + bool bDepVal = false; + if( pVal->Value >>= nDepVal ) + { + bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1); + } + else if( pVal->Value >>= bDepVal ) + { + // could be a dependency on a checked boolean + // in this case the dependency is on a non zero for checked value + bEnabled = ( bDepVal && it->second.mnDependsOnEntry != 0) || + ( ! bDepVal && it->second.mnDependsOnEntry == 0); + } + else + { + // if the type does not match something is awry + OSL_FAIL( "strange type in control dependency" ); + bEnabled = false; + } + } + } + } + } + } + return bEnabled; +} + +bool PrinterController::isUIChoiceEnabled( const OUString& i_rProperty, sal_Int32 i_nValue ) const +{ + bool bEnabled = true; + ImplPrinterControllerData::ChoiceDisableMap::const_iterator it = + mpImplData->maChoiceDisableMap.find( i_rProperty ); + if(it != mpImplData->maChoiceDisableMap.end() ) + { + const css::uno::Sequence< sal_Bool >& rDisabled( it->second ); + if( i_nValue >= 0 && i_nValue < rDisabled.getLength() ) + bEnabled = ! rDisabled[i_nValue]; + } + return bEnabled; +} + +OUString PrinterController::makeEnabled( const OUString& i_rProperty ) +{ + OUString aDependency; + + vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it = + mpImplData->maControlDependencies.find( i_rProperty ); + if( it != mpImplData->maControlDependencies.end() ) + { + if( isUIOptionEnabled( it->second.maDependsOnName ) ) + { + aDependency = it->second.maDependsOnName; + const css::beans::PropertyValue* pVal = getValue( aDependency ); + OSL_ENSURE( pVal, "unknown property in dependency" ); + if( pVal ) + { + sal_Int32 nDepVal = 0; + bool bDepVal = false; + if( pVal->Value >>= nDepVal ) + { + if( it->second.mnDependsOnEntry != -1 ) + { + setValue( aDependency, css::uno::Any( sal_Int32( it->second.mnDependsOnEntry ) ) ); + } + } + else if( pVal->Value >>= bDepVal ) + { + setValue( aDependency, css::uno::Any( it->second.mnDependsOnEntry != 0 ) ); + } + else + { + // if the type does not match something is awry + OSL_FAIL( "strange type in control dependency" ); + } + } + } + } + + return aDependency; +} + +void PrinterController::createProgressDialog() +{ + if (!mpImplData->mxProgress) + { + bool bShow = true; + css::beans::PropertyValue* pMonitor = getValue( "MonitorVisible" ); + if( pMonitor ) + pMonitor->Value >>= bShow; + else + { + const css::beans::PropertyValue* pVal = getValue( "IsApi" ); + if( pVal ) + { + bool bApi = false; + pVal->Value >>= bApi; + bShow = ! bApi; + } + } + + if( bShow && ! Application::IsHeadlessModeEnabled() ) + { + mpImplData->mxProgress = std::make_shared<PrintProgressDialog>(getWindow(), getPageCountProtected()); + weld::DialogController::runAsync(mpImplData->mxProgress, [](sal_Int32 /*nResult*/){}); + } + } + else + { + mpImplData->mxProgress->response(RET_CANCEL); + mpImplData->mxProgress.reset(); + } +} + +bool PrinterController::isProgressCanceled() const +{ + return mpImplData->mxProgress && mpImplData->mxProgress->isCanceled(); +} + +void PrinterController::setMultipage( const MultiPageSetup& i_rMPS ) +{ + mpImplData->maMultiPage = i_rMPS; +} + +const PrinterController::MultiPageSetup& PrinterController::getMultipage() const +{ + return mpImplData->maMultiPage; +} + +void PrinterController::resetPaperToLastConfigured() +{ + mpImplData->resetPaperToLastConfigured(); +} + +void PrinterController::pushPropertiesToPrinter() +{ + sal_Int32 nCopyCount = 1; + // set copycount and collate + const css::beans::PropertyValue* pVal = getValue( "CopyCount" ); + if( pVal ) + pVal->Value >>= nCopyCount; + bool bCollate = false; + pVal = getValue( "Collate" ); + if( pVal ) + pVal->Value >>= bCollate; + mpImplData->mxPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate ); + + pVal = getValue("SinglePrintJobs"); + bool bSinglePrintJobs = false; + if (pVal) + pVal->Value >>= bSinglePrintJobs; + mpImplData->mxPrinter->SetSinglePrintJobs(bSinglePrintJobs); + + // duplex mode + pVal = getValue( "DuplexMode" ); + if( pVal ) + { + sal_Int16 nDuplex = css::view::DuplexMode::UNKNOWN; + pVal->Value >>= nDuplex; + switch( nDuplex ) + { + case css::view::DuplexMode::OFF: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::Off ); break; + case css::view::DuplexMode::LONGEDGE: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::LongEdge ); break; + case css::view::DuplexMode::SHORTEDGE: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::ShortEdge ); break; + } + } +} + +bool PrinterController::isShowDialogs() const +{ + bool bApi = getBoolProperty( "IsApi", false ); + return ! bApi && ! Application::IsHeadlessModeEnabled(); +} + +bool PrinterController::isDirectPrint() const +{ + bool bDirect = getBoolProperty( "IsDirect", false ); + return bDirect; +} + +bool PrinterController::getBoolProperty( const OUString& i_rProperty, bool i_bFallback ) const +{ + bool bRet = i_bFallback; + const css::beans::PropertyValue* pVal = getValue( i_rProperty ); + if( pVal ) + pVal->Value >>= bRet; + return bRet; +} + +sal_Int32 PrinterController::getIntProperty( const OUString& i_rProperty, sal_Int32 i_nFallback ) const +{ + sal_Int32 nRet = i_nFallback; + const css::beans::PropertyValue* pVal = getValue( i_rProperty ); + if( pVal ) + pVal->Value >>= nRet; + return nRet; +} + +/* + * PrinterOptionsHelper +**/ +css::uno::Any PrinterOptionsHelper::getValue( const OUString& i_rPropertyName ) const +{ + css::uno::Any aRet; + std::unordered_map< OUString, css::uno::Any >::const_iterator it = + m_aPropertyMap.find( i_rPropertyName ); + if( it != m_aPropertyMap.end() ) + aRet = it->second; + return aRet; +} + +bool PrinterOptionsHelper::getBoolValue( const OUString& i_rPropertyName, bool i_bDefault ) const +{ + bool bRet = false; + css::uno::Any aVal( getValue( i_rPropertyName ) ); + return (aVal >>= bRet) ? bRet : i_bDefault; +} + +sal_Int64 PrinterOptionsHelper::getIntValue( const OUString& i_rPropertyName, sal_Int64 i_nDefault ) const +{ + sal_Int64 nRet = 0; + css::uno::Any aVal( getValue( i_rPropertyName ) ); + return (aVal >>= nRet) ? nRet : i_nDefault; +} + +OUString PrinterOptionsHelper::getStringValue( const OUString& i_rPropertyName ) const +{ + OUString aRet; + css::uno::Any aVal( getValue( i_rPropertyName ) ); + return (aVal >>= aRet) ? aRet : OUString(); +} + +bool PrinterOptionsHelper::processProperties( const css::uno::Sequence< css::beans::PropertyValue >& i_rNewProp ) +{ + bool bChanged = false; + + for( const auto& rVal : i_rNewProp ) + { + std::unordered_map< OUString, css::uno::Any >::iterator it = + m_aPropertyMap.find( rVal.Name ); + + bool bElementChanged = (it == m_aPropertyMap.end()) || (it->second != rVal.Value); + if( bElementChanged ) + { + m_aPropertyMap[ rVal.Name ] = rVal.Value; + bChanged = true; + } + } + return bChanged; +} + +void PrinterOptionsHelper::appendPrintUIOptions( css::uno::Sequence< css::beans::PropertyValue >& io_rProps ) const +{ + if( !m_aUIProperties.empty() ) + { + sal_Int32 nIndex = io_rProps.getLength(); + io_rProps.realloc( nIndex+1 ); + io_rProps.getArray()[ nIndex ] = comphelper::makePropertyValue( + "ExtraPrintUIOptions", comphelper::containerToSequence(m_aUIProperties)); + } +} + +css::uno::Any PrinterOptionsHelper::setUIControlOpt(const css::uno::Sequence< OUString >& i_rIDs, + const OUString& i_rTitle, + const css::uno::Sequence< OUString >& i_rHelpIds, + const OUString& i_rType, + const css::beans::PropertyValue* i_pVal, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + sal_Int32 nElements = + 2 // ControlType + ID + + (i_rTitle.isEmpty() ? 0 : 1) // Text + + (i_rHelpIds.hasElements() ? 1 : 0) // HelpId + + (i_pVal ? 1 : 0) // Property + + i_rControlOptions.maAddProps.size() // additional props + + (i_rControlOptions.maGroupHint.isEmpty() ? 0 : 1) // grouping + + (i_rControlOptions.mbInternalOnly ? 1 : 0) // internal hint + + (i_rControlOptions.mbEnabled ? 0 : 1) // enabled + ; + if( !i_rControlOptions.maDependsOnName.isEmpty() ) + { + nElements += 1; + if( i_rControlOptions.mnDependsOnEntry != -1 ) + nElements += 1; + if( i_rControlOptions.mbAttachToDependency ) + nElements += 1; + } + + css::uno::Sequence< css::beans::PropertyValue > aCtrl( nElements ); + auto pCtrl = aCtrl.getArray(); + sal_Int32 nUsed = 0; + if( !i_rTitle.isEmpty() ) + { + pCtrl[nUsed ].Name = "Text"; + pCtrl[nUsed++].Value <<= i_rTitle; + } + if( i_rHelpIds.hasElements() ) + { + pCtrl[nUsed ].Name = "HelpId"; + pCtrl[nUsed++].Value <<= i_rHelpIds; + } + pCtrl[nUsed ].Name = "ControlType"; + pCtrl[nUsed++].Value <<= i_rType; + pCtrl[nUsed ].Name = "ID"; + pCtrl[nUsed++].Value <<= i_rIDs; + if( i_pVal ) + { + pCtrl[nUsed ].Name = "Property"; + pCtrl[nUsed++].Value <<= *i_pVal; + } + if( !i_rControlOptions.maDependsOnName.isEmpty() ) + { + pCtrl[nUsed ].Name = "DependsOnName"; + pCtrl[nUsed++].Value <<= i_rControlOptions.maDependsOnName; + if( i_rControlOptions.mnDependsOnEntry != -1 ) + { + pCtrl[nUsed ].Name = "DependsOnEntry"; + pCtrl[nUsed++].Value <<= i_rControlOptions.mnDependsOnEntry; + } + if( i_rControlOptions.mbAttachToDependency ) + { + pCtrl[nUsed ].Name = "AttachToDependency"; + pCtrl[nUsed++].Value <<= i_rControlOptions.mbAttachToDependency; + } + } + if( !i_rControlOptions.maGroupHint.isEmpty() ) + { + pCtrl[nUsed ].Name = "GroupingHint"; + pCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint; + } + if( i_rControlOptions.mbInternalOnly ) + { + pCtrl[nUsed ].Name = "InternalUIOnly"; + pCtrl[nUsed++].Value <<= true; + } + if( ! i_rControlOptions.mbEnabled ) + { + pCtrl[nUsed ].Name = "Enabled"; + pCtrl[nUsed++].Value <<= false; + } + + sal_Int32 nAddProps = i_rControlOptions.maAddProps.size(); + for( sal_Int32 i = 0; i < nAddProps; i++ ) + pCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i]; + + SAL_WARN_IF( nUsed != nElements, "vcl.gdi", "nUsed != nElements, probable heap corruption" ); + + return css::uno::Any( aCtrl ); +} + +css::uno::Any PrinterOptionsHelper::setGroupControlOpt(const OUString& i_rID, + const OUString& i_rTitle, + const OUString& i_rHelpId) +{ + css::uno::Sequence< OUString > aHelpId; + if( !i_rHelpId.isEmpty() ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + css::uno::Sequence< OUString > aIds { i_rID }; + return setUIControlOpt(aIds, i_rTitle, aHelpId, "Group"); +} + +css::uno::Any PrinterOptionsHelper::setSubgroupControlOpt(const OUString& i_rID, + const OUString& i_rTitle, + const OUString& i_rHelpId, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + css::uno::Sequence< OUString > aHelpId; + if( !i_rHelpId.isEmpty() ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + css::uno::Sequence< OUString > aIds { i_rID }; + return setUIControlOpt(aIds, i_rTitle, aHelpId, "Subgroup", nullptr, i_rControlOptions); +} + +css::uno::Any PrinterOptionsHelper::setBoolControlOpt(const OUString& i_rID, + const OUString& i_rTitle, + const OUString& i_rHelpId, + const OUString& i_rProperty, + bool i_bValue, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + css::uno::Sequence< OUString > aHelpId; + if( !i_rHelpId.isEmpty() ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + css::beans::PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value <<= i_bValue; + css::uno::Sequence< OUString > aIds { i_rID }; + return setUIControlOpt(aIds, i_rTitle, aHelpId, "Bool", &aVal, i_rControlOptions); +} + +css::uno::Any PrinterOptionsHelper::setChoiceRadiosControlOpt(const css::uno::Sequence< OUString >& i_rIDs, + const OUString& i_rTitle, + const css::uno::Sequence< OUString >& i_rHelpId, + const OUString& i_rProperty, + const css::uno::Sequence< OUString >& i_rChoices, + sal_Int32 i_nValue, + const css::uno::Sequence< sal_Bool >& i_rDisabledChoices, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + UIControlOptions aOpt( i_rControlOptions ); + sal_Int32 nUsed = aOpt.maAddProps.size(); + aOpt.maAddProps.resize( nUsed + 1 + (i_rDisabledChoices.hasElements() ? 1 : 0) ); + aOpt.maAddProps[nUsed].Name = "Choices"; + aOpt.maAddProps[nUsed].Value <<= i_rChoices; + if( i_rDisabledChoices.hasElements() ) + { + aOpt.maAddProps[nUsed+1].Name = "ChoicesDisabled"; + aOpt.maAddProps[nUsed+1].Value <<= i_rDisabledChoices; + } + + css::beans::PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value <<= i_nValue; + return setUIControlOpt(i_rIDs, i_rTitle, i_rHelpId, "Radio", &aVal, aOpt); +} + +css::uno::Any PrinterOptionsHelper::setChoiceListControlOpt(const OUString& i_rID, + const OUString& i_rTitle, + const css::uno::Sequence< OUString >& i_rHelpId, + const OUString& i_rProperty, + const css::uno::Sequence< OUString >& i_rChoices, + sal_Int32 i_nValue, + const css::uno::Sequence< sal_Bool >& i_rDisabledChoices, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + UIControlOptions aOpt( i_rControlOptions ); + sal_Int32 nUsed = aOpt.maAddProps.size(); + aOpt.maAddProps.resize( nUsed + 1 + (i_rDisabledChoices.hasElements() ? 1 : 0) ); + aOpt.maAddProps[nUsed].Name = "Choices"; + aOpt.maAddProps[nUsed].Value <<= i_rChoices; + if( i_rDisabledChoices.hasElements() ) + { + aOpt.maAddProps[nUsed+1].Name = "ChoicesDisabled"; + aOpt.maAddProps[nUsed+1].Value <<= i_rDisabledChoices; + } + + css::beans::PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value <<= i_nValue; + css::uno::Sequence< OUString > aIds { i_rID }; + return setUIControlOpt(aIds, i_rTitle, i_rHelpId, "List", &aVal, aOpt); +} + +css::uno::Any PrinterOptionsHelper::setRangeControlOpt(const OUString& i_rID, + const OUString& i_rTitle, + const OUString& i_rHelpId, + const OUString& i_rProperty, + sal_Int32 i_nValue, + sal_Int32 i_nMinValue, + sal_Int32 i_nMaxValue, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + UIControlOptions aOpt( i_rControlOptions ); + if( i_nMaxValue >= i_nMinValue ) + { + sal_Int32 nUsed = aOpt.maAddProps.size(); + aOpt.maAddProps.resize( nUsed + 2 ); + aOpt.maAddProps[nUsed ].Name = "MinValue"; + aOpt.maAddProps[nUsed++].Value <<= i_nMinValue; + aOpt.maAddProps[nUsed ].Name = "MaxValue"; + aOpt.maAddProps[nUsed++].Value <<= i_nMaxValue; + } + + css::uno::Sequence< OUString > aHelpId; + if( !i_rHelpId.isEmpty() ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + css::beans::PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value <<= i_nValue; + css::uno::Sequence< OUString > aIds { i_rID }; + return setUIControlOpt(aIds, i_rTitle, aHelpId, "Range", &aVal, aOpt); +} + +css::uno::Any PrinterOptionsHelper::setEditControlOpt(const OUString& i_rID, + const OUString& i_rTitle, + const OUString& i_rHelpId, + const OUString& i_rProperty, + const OUString& i_rValue, + const PrinterOptionsHelper::UIControlOptions& i_rControlOptions) +{ + css::uno::Sequence< OUString > aHelpId; + if( !i_rHelpId.isEmpty() ) + { + aHelpId.realloc( 1 ); + *aHelpId.getArray() = i_rHelpId; + } + css::beans::PropertyValue aVal; + aVal.Name = i_rProperty; + aVal.Value <<= i_rValue; + css::uno::Sequence< OUString > aIds { i_rID }; + return setUIControlOpt(aIds, i_rTitle, aHelpId, "Edit", &aVal, i_rControlOptions); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |