1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <vcl/weld.hxx>
21 #include <vcl/print.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/metaact.hxx>
24 #include <configsettings.hxx>
25 #include <tools/urlobj.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/sequence.hxx>
28 #include <sal/types.h>
29 #include <sal/log.hxx>
30 #include <tools/debug.hxx>
31 #include <tools/diagnose_ex.h>
32
33 #include <printdlg.hxx>
34 #include <svdata.hxx>
35 #include <salinst.hxx>
36 #include <salprn.hxx>
37 #include <strings.hrc>
38
39 #include <com/sun/star/ui/dialogs/FilePicker.hpp>
40 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
41 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
42 #include <com/sun/star/view/DuplexMode.hpp>
43 #include <com/sun/star/lang/IllegalArgumentException.hpp>
44 #include <com/sun/star/awt/Size.hpp>
45
46 #include <unordered_map>
47 #include <unordered_set>
48
49 using namespace vcl;
50
51 namespace {
52
53 class ImplPageCache
54 {
55 struct CacheEntry
56 {
57 GDIMetaFile aPage;
58 PrinterController::PageSize aSize;
59 };
60
61 std::vector< CacheEntry > maPages;
62 std::vector< sal_Int32 > maPageNumbers;
63 std::vector< sal_Int32 > maCacheRanking;
64
65 static const sal_Int32 nCacheSize = 6;
66
updateRanking(sal_Int32 nLastHit)67 void updateRanking( sal_Int32 nLastHit )
68 {
69 if( maCacheRanking[0] != nLastHit )
70 {
71 for( sal_Int32 i = nCacheSize-1; i > 0; i-- )
72 maCacheRanking[i] = maCacheRanking[i-1];
73 maCacheRanking[0] = nLastHit;
74 }
75 }
76
77 public:
ImplPageCache()78 ImplPageCache()
79 : maPages( nCacheSize )
80 , maPageNumbers( nCacheSize, -1 )
81 , maCacheRanking( nCacheSize )
82 {
83 for( sal_Int32 i = 0; i < nCacheSize; i++ )
84 maCacheRanking[i] = nCacheSize - i - 1;
85 }
86
87 // caution: does not ensure uniqueness
insert(sal_Int32 i_nPageNo,const GDIMetaFile & i_rPage,const PrinterController::PageSize & i_rSize)88 void insert( sal_Int32 i_nPageNo, const GDIMetaFile& i_rPage, const PrinterController::PageSize& i_rSize )
89 {
90 sal_Int32 nReplacePage = maCacheRanking.back();
91 maPages[ nReplacePage ].aPage = i_rPage;
92 maPages[ nReplacePage ].aSize = i_rSize;
93 maPageNumbers[ nReplacePage ] = i_nPageNo;
94 // cache insertion means in our case, the page was just queried
95 // so update the ranking
96 updateRanking( nReplacePage );
97 }
98
99 // caution: bad algorithm; should there ever be reason to increase the cache size beyond 6
100 // this needs to be urgently rewritten. However do NOT increase the cache size lightly,
101 // whole pages can be rather memory intensive
get(sal_Int32 i_nPageNo,GDIMetaFile & o_rPageFile,PrinterController::PageSize & o_rSize)102 bool get( sal_Int32 i_nPageNo, GDIMetaFile& o_rPageFile, PrinterController::PageSize& o_rSize )
103 {
104 for( sal_Int32 i = 0; i < nCacheSize; ++i )
105 {
106 if( maPageNumbers[i] == i_nPageNo )
107 {
108 updateRanking( i );
109 o_rPageFile = maPages[i].aPage;
110 o_rSize = maPages[i].aSize;
111 return true;
112 }
113 }
114 return false;
115 }
116
invalidate()117 void invalidate()
118 {
119 for( sal_Int32 i = 0; i < nCacheSize; ++i )
120 {
121 maPageNumbers[i] = -1;
122 maPages[i].aPage.Clear();
123 maCacheRanking[i] = nCacheSize - i - 1;
124 }
125 }
126 };
127
128 }
129
130 class vcl::ImplPrinterControllerData
131 {
132 public:
133 struct ControlDependency
134 {
135 OUString maDependsOnName;
136 sal_Int32 mnDependsOnEntry;
137
ControlDependencyvcl::ImplPrinterControllerData::ControlDependency138 ControlDependency() : mnDependsOnEntry( -1 ) {}
139 };
140
141 typedef std::unordered_map< OUString, size_t > PropertyToIndexMap;
142 typedef std::unordered_map< OUString, ControlDependency > ControlDependencyMap;
143 typedef std::unordered_map< OUString, css::uno::Sequence< sal_Bool > > ChoiceDisableMap;
144
145 VclPtr< Printer > mxPrinter;
146 weld::Window* mpWindow;
147 css::uno::Sequence< css::beans::PropertyValue > maUIOptions;
148 std::vector< css::beans::PropertyValue > maUIProperties;
149 std::vector< bool > maUIPropertyEnabled;
150 PropertyToIndexMap maPropertyToIndex;
151 ControlDependencyMap maControlDependencies;
152 ChoiceDisableMap maChoiceDisableMap;
153 bool mbFirstPage;
154 bool mbLastPage;
155 bool mbReversePageOrder;
156 bool mbPapersizeFromSetup;
157 bool mbPapersizeFromUser;
158 bool mbPrinterModified;
159 css::view::PrintableState meJobState;
160
161 vcl::PrinterController::MultiPageSetup maMultiPage;
162
163 std::shared_ptr<vcl::PrintProgressDialog> mxProgress;
164
165 ImplPageCache maPageCache;
166
167 // set by user through printer properties subdialog of printer settings dialog
168 Size maDefaultPageSize;
169 // set by user through print dialog
170 Size maUserPageSize;
171 // set by user through printer properties subdialog of printer settings dialog
172 sal_Int32 mnDefaultPaperBin;
173 // Set by user through printer properties subdialog of print dialog.
174 // Overrides application-set tray for a page.
175 sal_Int32 mnFixedPaperBin;
176
177 // N.B. Apparently we have three levels of paper tray settings
178 // (latter overrides former):
179 // 1. default tray
180 // 2. tray set for a concrete page by an application, e.g., writer
181 // allows setting a printer tray (for the default printer) for a
182 // page style. This setting can be overridden by user by selecting
183 // "Use only paper tray from printer preferences" on the Options
184 // page in the print dialog, in which case the default tray is
185 // used for all pages.
186 // 3. tray set in printer properties the printer dialog
187 // I'm not quite sure why 1. and 3. are distinct, but the commit
188 // history suggests this is intentional...
189
ImplPrinterControllerData()190 ImplPrinterControllerData() :
191 mpWindow( nullptr ),
192 mbFirstPage( true ),
193 mbLastPage( false ),
194 mbReversePageOrder( false ),
195 mbPapersizeFromSetup( false ),
196 mbPapersizeFromUser( false ),
197 mbPrinterModified( false ),
198 meJobState( css::view::PrintableState_JOB_STARTED ),
199 mnDefaultPaperBin( -1 ),
200 mnFixedPaperBin( -1 )
201 {}
202
~ImplPrinterControllerData()203 ~ImplPrinterControllerData()
204 {
205 if (mxProgress)
206 {
207 mxProgress->response(RET_CANCEL);
208 mxProgress.reset();
209 }
210 }
211
getRealPaperSize(const Size & i_rPageSize,bool bNoNUP) const212 const Size& getRealPaperSize( const Size& i_rPageSize, bool bNoNUP ) const
213 {
214 if ( mbPapersizeFromUser )
215 return maUserPageSize;
216 if( mbPapersizeFromSetup )
217 return maDefaultPageSize;
218 if( maMultiPage.nRows * maMultiPage.nColumns > 1 && ! bNoNUP )
219 return maMultiPage.aPaperSize;
220 return i_rPageSize;
221 }
222 PrinterController::PageSize modifyJobSetup( const css::uno::Sequence< css::beans::PropertyValue >& i_rProps );
223 void resetPaperToLastConfigured();
224 };
225
PrinterController(const VclPtr<Printer> & i_xPrinter,weld::Window * i_pWindow)226 PrinterController::PrinterController(const VclPtr<Printer>& i_xPrinter, weld::Window* i_pWindow)
227 : mpImplData( new ImplPrinterControllerData )
228 {
229 mpImplData->mxPrinter = i_xPrinter;
230 mpImplData->mpWindow = i_pWindow;
231 }
232
queryFile(Printer const * pPrinter)233 static OUString queryFile( Printer const * pPrinter )
234 {
235 OUString aResult;
236
237 css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
238 css::uno::Reference< css::ui::dialogs::XFilePicker3 > xFilePicker = css::ui::dialogs::FilePicker::createWithMode(xContext, css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION);
239
240 try
241 {
242 #ifdef UNX
243 // add PostScript and PDF
244 bool bPS = true, bPDF = true;
245 if( pPrinter )
246 {
247 if( pPrinter->GetCapabilities( PrinterCapType::PDF ) )
248 bPS = false;
249 else
250 bPDF = false;
251 }
252 if( bPS )
253 xFilePicker->appendFilter( "PostScript", "*.ps" );
254 if( bPDF )
255 xFilePicker->appendFilter( "Portable Document Format", "*.pdf" );
256 #elif defined _WIN32
257 (void)pPrinter;
258 xFilePicker->appendFilter( "*.PRN", "*.prn" );
259 #endif
260 // add arbitrary files
261 xFilePicker->appendFilter(VclResId(SV_STDTEXT_ALLFILETYPES), "*.*");
262 }
263 catch (const css::lang::IllegalArgumentException&)
264 {
265 TOOLS_WARN_EXCEPTION( "vcl.gdi", "caught IllegalArgumentException when registering filter" );
266 }
267
268 if( xFilePicker->execute() == css::ui::dialogs::ExecutableDialogResults::OK )
269 {
270 css::uno::Sequence< OUString > aPathSeq( xFilePicker->getSelectedFiles() );
271 INetURLObject aObj( aPathSeq[0] );
272 aResult = aObj.PathToFileName();
273 }
274 return aResult;
275 }
276
277 namespace {
278
279 struct PrintJobAsync
280 {
281 std::shared_ptr<PrinterController> mxController;
282 JobSetup maInitSetup;
283
PrintJobAsync__anone572c6250211::PrintJobAsync284 PrintJobAsync(const std::shared_ptr<PrinterController>& i_xController,
285 const JobSetup& i_rInitSetup)
286 : mxController( i_xController ), maInitSetup( i_rInitSetup )
287 {}
288
289 DECL_LINK( ExecJob, void*, void );
290 };
291
292 }
293
IMPL_LINK_NOARG(PrintJobAsync,ExecJob,void *,void)294 IMPL_LINK_NOARG(PrintJobAsync, ExecJob, void*, void)
295 {
296 Printer::ImplPrintJob(mxController, maInitSetup);
297
298 // clean up, do not access members after this
299 delete this;
300 }
301
PrintJob(const std::shared_ptr<PrinterController> & i_xController,const JobSetup & i_rInitSetup)302 void Printer::PrintJob(const std::shared_ptr<PrinterController>& i_xController,
303 const JobSetup& i_rInitSetup)
304 {
305 bool bSynchronous = false;
306 css::beans::PropertyValue* pVal = i_xController->getValue( "Wait" );
307 if( pVal )
308 pVal->Value >>= bSynchronous;
309
310 if( bSynchronous )
311 ImplPrintJob(i_xController, i_rInitSetup);
312 else
313 {
314 PrintJobAsync* pAsync = new PrintJobAsync(i_xController, i_rInitSetup);
315 Application::PostUserEvent( LINK( pAsync, PrintJobAsync, ExecJob ) );
316 }
317 }
318
PreparePrintJob(std::shared_ptr<PrinterController> xController,const JobSetup & i_rInitSetup)319 bool Printer::PreparePrintJob(std::shared_ptr<PrinterController> xController,
320 const JobSetup& i_rInitSetup)
321 {
322 // check if there is a default printer; if not, show an error box (if appropriate)
323 if( GetDefaultPrinterName().isEmpty() )
324 {
325 if (xController->isShowDialogs())
326 {
327 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(xController->getWindow(), "vcl/ui/errornoprinterdialog.ui"));
328 std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorNoPrinterDialog"));
329 xBox->run();
330 }
331 xController->setValue( "IsDirect",
332 css::uno::makeAny( false ) );
333 }
334
335 // setup printer
336
337 // #i114306# changed behavior back from persistence
338 // if no specific printer is already set, create the default printer
339 if (!xController->getPrinter())
340 {
341 OUString aPrinterName( i_rInitSetup.GetPrinterName() );
342 VclPtrInstance<Printer> xPrinter( aPrinterName );
343 xPrinter->SetJobSetup(i_rInitSetup);
344 xController->setPrinter(xPrinter);
345 xController->setPapersizeFromSetup(xPrinter->GetPrinterSettingsPreferred());
346 }
347
348 // reset last page property
349 xController->setLastPage(false);
350
351 // update "PageRange" property inferring from other properties:
352 // case 1: "Pages" set from UNO API ->
353 // setup "Print Selection" and insert "PageRange" attribute
354 // case 2: "All pages" is selected
355 // update "Page range" attribute to have a sensible default,
356 // but leave "All" as selected
357
358 // "Pages" attribute from API is now equivalent to "PageRange"
359 // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
360 // Argh ! That sure needs cleaning up
361 css::beans::PropertyValue* pContentVal = xController->getValue("PrintRange");
362 if( ! pContentVal )
363 pContentVal = xController->getValue("PrintContent");
364
365 // case 1: UNO API has set "Pages"
366 css::beans::PropertyValue* pPagesVal = xController->getValue("Pages");
367 if( pPagesVal )
368 {
369 OUString aPagesVal;
370 pPagesVal->Value >>= aPagesVal;
371 if( !aPagesVal.isEmpty() )
372 {
373 // "Pages" attribute from API is now equivalent to "PageRange"
374 // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
375 // Argh ! That sure needs cleaning up
376 if( pContentVal )
377 {
378 pContentVal->Value <<= sal_Int32( 1 );
379 xController->setValue("PageRange", pPagesVal->Value);
380 }
381 }
382 }
383 // case 2: is "All" selected ?
384 else if( pContentVal )
385 {
386 sal_Int32 nContent = -1;
387 if( pContentVal->Value >>= nContent )
388 {
389 if( nContent == 0 )
390 {
391 // do not overwrite PageRange if it is already set
392 css::beans::PropertyValue* pRangeVal = xController->getValue("PageRange");
393 OUString aRange;
394 if( pRangeVal )
395 pRangeVal->Value >>= aRange;
396 if( aRange.isEmpty() )
397 {
398 sal_Int32 nPages = xController->getPageCount();
399 if( nPages > 0 )
400 {
401 OUStringBuffer aBuf( 32 );
402 aBuf.append( "1" );
403 if( nPages > 1 )
404 {
405 aBuf.append( "-" );
406 aBuf.append( nPages );
407 }
408 xController->setValue("PageRange", css::uno::makeAny(aBuf.makeStringAndClear()));
409 }
410 }
411 }
412 }
413 }
414
415 css::beans::PropertyValue* pReverseVal = xController->getValue("PrintReverse");
416 if( pReverseVal )
417 {
418 bool bReverse = false;
419 pReverseVal->Value >>= bReverse;
420 xController->setReversePrint( bReverse );
421 }
422
423 css::beans::PropertyValue* pPapersizeFromSetupVal = xController->getValue("PapersizeFromSetup");
424 if( pPapersizeFromSetupVal )
425 {
426 bool bPapersizeFromSetup = false;
427 pPapersizeFromSetupVal->Value >>= bPapersizeFromSetup;
428 xController->setPapersizeFromSetup(bPapersizeFromSetup);
429 }
430
431 // setup NUp printing from properties
432 sal_Int32 nRows = xController->getIntProperty("NUpRows", 1);
433 sal_Int32 nCols = xController->getIntProperty("NUpColumns", 1);
434 if( nRows > 1 || nCols > 1 )
435 {
436 PrinterController::MultiPageSetup aMPS;
437 aMPS.nRows = std::max<sal_Int32>(nRows, 1);
438 aMPS.nColumns = std::max<sal_Int32>(nCols, 1);
439 sal_Int32 nValue = xController->getIntProperty("NUpPageMarginLeft", aMPS.nLeftMargin);
440 if( nValue >= 0 )
441 aMPS.nLeftMargin = nValue;
442 nValue = xController->getIntProperty("NUpPageMarginRight", aMPS.nRightMargin);
443 if( nValue >= 0 )
444 aMPS.nRightMargin = nValue;
445 nValue = xController->getIntProperty( "NUpPageMarginTop", aMPS.nTopMargin );
446 if( nValue >= 0 )
447 aMPS.nTopMargin = nValue;
448 nValue = xController->getIntProperty( "NUpPageMarginBottom", aMPS.nBottomMargin );
449 if( nValue >= 0 )
450 aMPS.nBottomMargin = nValue;
451 nValue = xController->getIntProperty( "NUpHorizontalSpacing", aMPS.nHorizontalSpacing );
452 if( nValue >= 0 )
453 aMPS.nHorizontalSpacing = nValue;
454 nValue = xController->getIntProperty( "NUpVerticalSpacing", aMPS.nVerticalSpacing );
455 if( nValue >= 0 )
456 aMPS.nVerticalSpacing = nValue;
457 aMPS.bDrawBorder = xController->getBoolProperty( "NUpDrawBorder", aMPS.bDrawBorder );
458 aMPS.nOrder = static_cast<NupOrderType>(xController->getIntProperty( "NUpSubPageOrder", static_cast<sal_Int32>(aMPS.nOrder) ));
459 aMPS.aPaperSize = xController->getPrinter()->PixelToLogic( xController->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) );
460 css::beans::PropertyValue* pPgSizeVal = xController->getValue( "NUpPaperSize" );
461 css::awt::Size aSizeVal;
462 if( pPgSizeVal && (pPgSizeVal->Value >>= aSizeVal) )
463 {
464 aMPS.aPaperSize.setWidth( aSizeVal.Width );
465 aMPS.aPaperSize.setHeight( aSizeVal.Height );
466 }
467
468 xController->setMultipage( aMPS );
469 }
470
471 // in direct print case check whether there is anything to print.
472 // if not, show an errorbox (if appropriate)
473 if( xController->isShowDialogs() && xController->isDirectPrint() )
474 {
475 if( xController->getFilteredPageCount() == 0 )
476 {
477 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(xController->getWindow(), "vcl/ui/errornocontentdialog.ui"));
478 std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorNoContentDialog"));
479 xBox->run();
480 return false;
481 }
482 }
483
484 // check if the printer brings up its own dialog
485 // in that case leave the work to that dialog
486 if( ! xController->getPrinter()->GetCapabilities( PrinterCapType::ExternalDialog ) &&
487 ! xController->isDirectPrint() &&
488 xController->isShowDialogs()
489 )
490 {
491 try
492 {
493 PrintDialog aDlg(xController->getWindow(), xController);
494 if (!aDlg.run())
495 {
496 xController->abortJob();
497 return false;
498 }
499 if (aDlg.isPrintToFile())
500 {
501 OUString aFile = queryFile( xController->getPrinter().get() );
502 if( aFile.isEmpty() )
503 {
504 xController->abortJob();
505 return false;
506 }
507 xController->setValue( "LocalFileName",
508 css::uno::makeAny( aFile ) );
509 }
510 else if (aDlg.isSingleJobs())
511 {
512 xController->getPrinter()->SetSinglePrintJobs(true);
513 }
514 }
515 catch (const std::bad_alloc&)
516 {
517 }
518 }
519
520 xController->pushPropertiesToPrinter();
521 return true;
522 }
523
ExecutePrintJob(const std::shared_ptr<PrinterController> & xController)524 bool Printer::ExecutePrintJob(const std::shared_ptr<PrinterController>& xController)
525 {
526 OUString aJobName;
527 css::beans::PropertyValue* pJobNameVal = xController->getValue( "JobName" );
528 if( pJobNameVal )
529 pJobNameVal->Value >>= aJobName;
530
531 return xController->getPrinter()->StartJob( aJobName, xController );
532 }
533
FinishPrintJob(const std::shared_ptr<PrinterController> & xController)534 void Printer::FinishPrintJob(const std::shared_ptr<PrinterController>& xController)
535 {
536 xController->resetPaperToLastConfigured();
537 xController->jobFinished( xController->getJobState() );
538 }
539
ImplPrintJob(const std::shared_ptr<PrinterController> & xController,const JobSetup & i_rInitSetup)540 void Printer::ImplPrintJob(const std::shared_ptr<PrinterController>& xController,
541 const JobSetup& i_rInitSetup)
542 {
543 if (PreparePrintJob(xController, i_rInitSetup))
544 {
545 ExecutePrintJob(xController);
546 }
547 FinishPrintJob(xController);
548 }
549
StartJob(const OUString & i_rJobName,std::shared_ptr<vcl::PrinterController> const & i_xController)550 bool Printer::StartJob( const OUString& i_rJobName, std::shared_ptr<vcl::PrinterController> const & i_xController)
551 {
552 mnError = ERRCODE_NONE;
553
554 if ( IsDisplayPrinter() )
555 return false;
556
557 if ( IsJobActive() || IsPrinting() )
558 return false;
559
560 sal_uInt32 nCopies = mnCopyCount;
561 bool bCollateCopy = mbCollateCopy;
562 bool bUserCopy = false;
563
564 if ( nCopies > 1 )
565 {
566 const sal_uInt32 nDevCopy = GetCapabilities( bCollateCopy
567 ? PrinterCapType::CollateCopies
568 : PrinterCapType::Copies );
569
570 // need to do copies by hand ?
571 if ( nCopies > nDevCopy )
572 {
573 bUserCopy = true;
574 nCopies = 1;
575 bCollateCopy = false;
576 }
577 }
578 else
579 bCollateCopy = false;
580
581 ImplSVData* pSVData = ImplGetSVData();
582 mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
583
584 if (!mpPrinter)
585 return false;
586
587 bool bSinglePrintJobs = i_xController->getPrinter()->IsSinglePrintJobs();
588
589 css::beans::PropertyValue* pFileValue = i_xController->getValue("LocalFileName");
590 if( pFileValue )
591 {
592 OUString aFile;
593 pFileValue->Value >>= aFile;
594 if( !aFile.isEmpty() )
595 {
596 mbPrintFile = true;
597 maPrintFile = aFile;
598 bSinglePrintJobs = false;
599 }
600 }
601
602 OUString* pPrintFile = nullptr;
603 if ( mbPrintFile )
604 pPrintFile = &maPrintFile;
605 mpPrinterOptions->ReadFromConfig( mbPrintFile );
606
607 mbPrinting = true;
608 if( GetCapabilities( PrinterCapType::UsePullModel ) )
609 {
610 mbJobActive = true;
611 // SAL layer does all necessary page printing
612 // and also handles showing a dialog
613 // that also means it must call jobStarted when the dialog is finished
614 // it also must set the JobState of the Controller
615 if( mpPrinter->StartJob( pPrintFile,
616 i_rJobName,
617 Application::GetDisplayName(),
618 &maJobSetup.ImplGetData(),
619 *i_xController) )
620 {
621 EndJob();
622 }
623 else
624 {
625 mnError = ImplSalPrinterErrorCodeToVCL(mpPrinter->GetErrorCode());
626 if ( !mnError )
627 mnError = PRINTER_GENERALERROR;
628 mbPrinting = false;
629 mpPrinter.reset();
630 mbJobActive = false;
631
632 GDIMetaFile aDummyFile;
633 i_xController->setLastPage(true);
634 i_xController->getFilteredPageFile(0, aDummyFile);
635
636 return false;
637 }
638 }
639 else
640 {
641 // possibly a dialog has been shown
642 // now the real job starts
643 i_xController->setJobState( css::view::PrintableState_JOB_STARTED );
644 i_xController->jobStarted();
645
646 int nJobs = 1;
647 int nOuterRepeatCount = 1;
648 int nInnerRepeatCount = 1;
649 if( bUserCopy )
650 {
651 if( mbCollateCopy )
652 nOuterRepeatCount = mnCopyCount;
653 else
654 nInnerRepeatCount = mnCopyCount;
655 }
656 if( bSinglePrintJobs )
657 {
658 nJobs = mnCopyCount;
659 nCopies = 1;
660 nOuterRepeatCount = nInnerRepeatCount = 1;
661 }
662
663 for( int nJobIteration = 0; nJobIteration < nJobs; nJobIteration++ )
664 {
665 bool bError = false;
666 if( mpPrinter->StartJob( pPrintFile,
667 i_rJobName,
668 Application::GetDisplayName(),
669 nCopies,
670 bCollateCopy,
671 i_xController->isDirectPrint(),
672 &maJobSetup.ImplGetData() ) )
673 {
674 bool bAborted = false;
675 mbJobActive = true;
676 i_xController->createProgressDialog();
677 const int nPages = i_xController->getFilteredPageCount();
678 // abort job, if no pages will be printed.
679 if ( nPages == 0 )
680 {
681 i_xController->abortJob();
682 bAborted = true;
683 }
684 for( int nOuterIteration = 0; nOuterIteration < nOuterRepeatCount && ! bAborted; nOuterIteration++ )
685 {
686 for( int nPage = 0; nPage < nPages && ! bAborted; nPage++ )
687 {
688 for( int nInnerIteration = 0; nInnerIteration < nInnerRepeatCount && ! bAborted; nInnerIteration++ )
689 {
690 if( nPage == nPages-1 &&
691 nOuterIteration == nOuterRepeatCount-1 &&
692 nInnerIteration == nInnerRepeatCount-1 &&
693 nJobIteration == nJobs-1 )
694 {
695 i_xController->setLastPage(true);
696 }
697 i_xController->printFilteredPage(nPage);
698 if (i_xController->isProgressCanceled())
699 {
700 i_xController->abortJob();
701 }
702 if (i_xController->getJobState() ==
703 css::view::PrintableState_JOB_ABORTED)
704 {
705 bAborted = true;
706 }
707 }
708 }
709 // FIXME: duplex ?
710 }
711 EndJob();
712
713 if( nJobIteration < nJobs-1 )
714 {
715 mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
716
717 if ( mpPrinter )
718 mbPrinting = true;
719 else
720 bError = true;
721 }
722 }
723 else
724 bError = true;
725
726 if( bError )
727 {
728 mnError = mpPrinter ? ImplSalPrinterErrorCodeToVCL(mpPrinter->GetErrorCode()) : ERRCODE_NONE;
729 if ( !mnError )
730 mnError = PRINTER_GENERALERROR;
731 i_xController->setJobState( mnError == PRINTER_ABORT
732 ? css::view::PrintableState_JOB_ABORTED
733 : css::view::PrintableState_JOB_FAILED );
734 mbPrinting = false;
735 mpPrinter.reset();
736
737 return false;
738 }
739 }
740
741 if (i_xController->getJobState() == css::view::PrintableState_JOB_STARTED)
742 i_xController->setJobState(css::view::PrintableState_JOB_SPOOLED);
743 }
744
745 // make last used printer persistent for UI jobs
746 if (i_xController->isShowDialogs() && !i_xController->isDirectPrint())
747 {
748 SettingsConfigItem* pItem = SettingsConfigItem::get();
749 pItem->setValue( "PrintDialog",
750 "LastPrinterUsed",
751 GetName()
752 );
753 }
754
755 return true;
756 }
757
~PrinterController()758 PrinterController::~PrinterController()
759 {
760 }
761
getJobState() const762 css::view::PrintableState PrinterController::getJobState() const
763 {
764 return mpImplData->meJobState;
765 }
766
setJobState(css::view::PrintableState i_eState)767 void PrinterController::setJobState( css::view::PrintableState i_eState )
768 {
769 mpImplData->meJobState = i_eState;
770 }
771
getPrinter() const772 const VclPtr<Printer>& PrinterController::getPrinter() const
773 {
774 return mpImplData->mxPrinter;
775 }
776
getWindow() const777 weld::Window* PrinterController::getWindow() const
778 {
779 return mpImplData->mpWindow;
780 }
781
dialogsParentClosing()782 void PrinterController::dialogsParentClosing()
783 {
784 mpImplData->mpWindow = nullptr;
785 if (mpImplData->mxProgress)
786 {
787 // close the dialog without doing anything, just get rid of it
788 mpImplData->mxProgress->response(RET_OK);
789 mpImplData->mxProgress.reset();
790 }
791 }
792
setPrinter(const VclPtr<Printer> & i_rPrinter)793 void PrinterController::setPrinter( const VclPtr<Printer>& i_rPrinter )
794 {
795 VclPtr<Printer> xPrinter = mpImplData->mxPrinter;
796
797 Size aPaperSize; // Save current paper size
798 Orientation eOrientation = Orientation::Portrait; // Save current paper orientation
799 bool bSavedSizeOrientation = false;
800
801 // #tdf 126744 Transfer paper size and orientation settings to newly selected printer
802 if ( xPrinter )
803 {
804 aPaperSize = xPrinter->GetPaperSize();
805 eOrientation = xPrinter->GetOrientation();
806 bSavedSizeOrientation = true;
807 }
808
809 mpImplData->mxPrinter = i_rPrinter;
810 setValue( "Name",
811 css::uno::makeAny( i_rPrinter->GetName() ) );
812 mpImplData->mnDefaultPaperBin = mpImplData->mxPrinter->GetPaperBin();
813 mpImplData->mxPrinter->Push();
814 mpImplData->mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
815 mpImplData->maDefaultPageSize = mpImplData->mxPrinter->GetPaperSize();
816
817 if ( bSavedSizeOrientation )
818 {
819 mpImplData->mxPrinter->SetPaperSizeUser(aPaperSize);
820 mpImplData->mxPrinter->SetOrientation(eOrientation);
821 }
822
823 mpImplData->mbPapersizeFromUser = false;
824 mpImplData->mxPrinter->Pop();
825 mpImplData->mnFixedPaperBin = -1;
826 }
827
resetPrinterOptions(bool i_bFileOutput)828 void PrinterController::resetPrinterOptions( bool i_bFileOutput )
829 {
830 PrinterOptions aOpt;
831 aOpt.ReadFromConfig( i_bFileOutput );
832 mpImplData->mxPrinter->SetPrinterOptions( aOpt );
833 }
834
setupPrinter(weld::Window * i_pParent)835 void PrinterController::setupPrinter( weld::Window* i_pParent )
836 {
837 bool bRet = false;
838
839 // Important to hold printer alive while doing setup etc.
840 VclPtr< Printer > xPrinter = mpImplData->mxPrinter;
841
842 if( !xPrinter )
843 return;
844
845 xPrinter->Push();
846 xPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
847
848 // get current data
849 Size aPaperSize(xPrinter->GetPaperSize());
850 Orientation eOrientation = xPrinter->GetOrientation();
851 sal_uInt16 nPaperBin = xPrinter->GetPaperBin();
852
853 // reset paper size back to last configured size, not
854 // whatever happens to be the current page
855 // (but only if the printer config has changed, otherwise
856 // don't override printer page auto-detection - tdf#91362)
857 if (getPrinterModified() || getPapersizeFromSetup())
858 {
859 resetPaperToLastConfigured();
860 }
861
862 // call driver setup
863 bRet = xPrinter->Setup( i_pParent, PrinterSetupMode::SingleJob );
864 SAL_WARN_IF(xPrinter != mpImplData->mxPrinter, "vcl.gdi",
865 "Printer changed underneath us during setup");
866 xPrinter = mpImplData->mxPrinter;
867
868 Size aNewPaperSize(xPrinter->GetPaperSize());
869 if (bRet)
870 {
871 bool bInvalidateCache = false;
872 setPapersizeFromSetup(xPrinter->GetPrinterSettingsPreferred());
873
874 // was papersize overridden ? if so we need to take action if we're
875 // configured to use the driver papersize
876 if (aNewPaperSize != mpImplData->maDefaultPageSize)
877 {
878 mpImplData->maDefaultPageSize = aNewPaperSize;
879 bInvalidateCache = getPapersizeFromSetup();
880 }
881
882 // was bin overridden ? if so we need to take action
883 sal_uInt16 nNewPaperBin = xPrinter->GetPaperBin();
884 if (nNewPaperBin != nPaperBin)
885 {
886 mpImplData->mnFixedPaperBin = nNewPaperBin;
887 bInvalidateCache = true;
888 }
889
890 if (bInvalidateCache)
891 {
892 mpImplData->maPageCache.invalidate();
893 }
894 }
895 else
896 {
897 //restore to whatever it was before we entered this method
898 xPrinter->SetOrientation( eOrientation );
899 if (aPaperSize != aNewPaperSize)
900 xPrinter->SetPaperSizeUser(aPaperSize);
901 }
902 xPrinter->Pop();
903 }
904
modifyJobSetup(const css::uno::Sequence<css::beans::PropertyValue> & i_rProps)905 PrinterController::PageSize vcl::ImplPrinterControllerData::modifyJobSetup( const css::uno::Sequence< css::beans::PropertyValue >& i_rProps )
906 {
907 PrinterController::PageSize aPageSize;
908 aPageSize.aSize = mxPrinter->GetPaperSize();
909 css::awt::Size aSetSize, aIsSize;
910 sal_Int32 nPaperBin = mnDefaultPaperBin;
911 for( const auto& rProp : i_rProps )
912 {
913 if ( rProp.Name == "PreferredPageSize" )
914 {
915 rProp.Value >>= aSetSize;
916 }
917 else if ( rProp.Name == "PageSize" )
918 {
919 rProp.Value >>= aIsSize;
920 }
921 else if ( rProp.Name == "PageIncludesNonprintableArea" )
922 {
923 bool bVal = false;
924 rProp.Value >>= bVal;
925 aPageSize.bFullPaper = bVal;
926 }
927 else if ( rProp.Name == "PrinterPaperTray" )
928 {
929 sal_Int32 nBin = -1;
930 rProp.Value >>= nBin;
931 if( nBin >= 0 && nBin < static_cast<sal_Int32>(mxPrinter->GetPaperBinCount()) )
932 nPaperBin = nBin;
933 }
934 }
935
936 Size aCurSize( mxPrinter->GetPaperSize() );
937 if( aSetSize.Width && aSetSize.Height )
938 {
939 Size aSetPaperSize( aSetSize.Width, aSetSize.Height );
940 Size aRealPaperSize( getRealPaperSize( aSetPaperSize, true/*bNoNUP*/ ) );
941 if( aRealPaperSize != aCurSize )
942 aIsSize = aSetSize;
943 }
944
945 if( aIsSize.Width && aIsSize.Height )
946 {
947 aPageSize.aSize.setWidth( aIsSize.Width );
948 aPageSize.aSize.setHeight( aIsSize.Height );
949
950 Size aRealPaperSize( getRealPaperSize( aPageSize.aSize, true/*bNoNUP*/ ) );
951 if( aRealPaperSize != aCurSize )
952 mxPrinter->SetPaperSizeUser( aRealPaperSize );
953 }
954
955 // paper bin set from properties in print dialog overrides
956 // application default for a page
957 if ( mnFixedPaperBin != -1 )
958 nPaperBin = mnFixedPaperBin;
959
960 if( nPaperBin != -1 && nPaperBin != mxPrinter->GetPaperBin() )
961 mxPrinter->SetPaperBin( nPaperBin );
962
963 return aPageSize;
964 }
965
966 //fdo#61886
967
968 //when printing is finished, set the paper size of the printer to either what
969 //the user explicitly set as the desired paper size, or fallback to whatever
970 //the printer had before printing started. That way it doesn't contain the last
971 //paper size of a multiple paper size using document when we are in our normal
972 //auto accept document paper size mode and end up overwriting the original
973 //paper size setting for file->printer_settings just by pressing "ok" in the
974 //print dialog
resetPaperToLastConfigured()975 void vcl::ImplPrinterControllerData::resetPaperToLastConfigured()
976 {
977 mxPrinter->Push();
978 mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
979 Size aCurSize(mxPrinter->GetPaperSize());
980 if (aCurSize != maDefaultPageSize)
981 mxPrinter->SetPaperSizeUser(maDefaultPageSize);
982 mxPrinter->Pop();
983 }
984
getPageCountProtected() const985 int PrinterController::getPageCountProtected() const
986 {
987 const MapMode aMapMode( MapUnit::Map100thMM );
988
989 mpImplData->mxPrinter->Push();
990 mpImplData->mxPrinter->SetMapMode( aMapMode );
991 int nPages = getPageCount();
992 mpImplData->mxPrinter->Pop();
993 return nPages;
994 }
995
getPageParametersProtected(int i_nPage) const996 css::uno::Sequence< css::beans::PropertyValue > PrinterController::getPageParametersProtected( int i_nPage ) const
997 {
998 const MapMode aMapMode( MapUnit::Map100thMM );
999
1000 mpImplData->mxPrinter->Push();
1001 mpImplData->mxPrinter->SetMapMode( aMapMode );
1002 css::uno::Sequence< css::beans::PropertyValue > aResult( getPageParameters( i_nPage ) );
1003 mpImplData->mxPrinter->Pop();
1004 return aResult;
1005 }
1006
getPageFile(int i_nUnfilteredPage,GDIMetaFile & o_rMtf,bool i_bMayUseCache)1007 PrinterController::PageSize PrinterController::getPageFile( int i_nUnfilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
1008 {
1009 // update progress if necessary
1010 if( mpImplData->mxProgress )
1011 {
1012 // do nothing if printing is canceled
1013 if( mpImplData->mxProgress->isCanceled() )
1014 return PrinterController::PageSize();
1015 mpImplData->mxProgress->tick();
1016 Application::Reschedule( true );
1017 }
1018
1019 if( i_bMayUseCache )
1020 {
1021 PrinterController::PageSize aPageSize;
1022 if( mpImplData->maPageCache.get( i_nUnfilteredPage, o_rMtf, aPageSize ) )
1023 {
1024 return aPageSize;
1025 }
1026 }
1027 else
1028 mpImplData->maPageCache.invalidate();
1029
1030 o_rMtf.Clear();
1031
1032 // get page parameters
1033 css::uno::Sequence< css::beans::PropertyValue > aPageParm( getPageParametersProtected( i_nUnfilteredPage ) );
1034 const MapMode aMapMode( MapUnit::Map100thMM );
1035
1036 mpImplData->mxPrinter->Push();
1037 mpImplData->mxPrinter->SetMapMode( aMapMode );
1038
1039 // modify job setup if necessary
1040 PrinterController::PageSize aPageSize = mpImplData->modifyJobSetup( aPageParm );
1041
1042 o_rMtf.SetPrefSize( aPageSize.aSize );
1043 o_rMtf.SetPrefMapMode( aMapMode );
1044
1045 mpImplData->mxPrinter->EnableOutput( false );
1046
1047 o_rMtf.Record( mpImplData->mxPrinter.get() );
1048
1049 printPage( i_nUnfilteredPage );
1050
1051 o_rMtf.Stop();
1052 o_rMtf.WindStart();
1053 mpImplData->mxPrinter->Pop();
1054
1055 if( i_bMayUseCache )
1056 mpImplData->maPageCache.insert( i_nUnfilteredPage, o_rMtf, aPageSize );
1057
1058 // reset "FirstPage" property to false now we've gotten at least our first one
1059 mpImplData->mbFirstPage = false;
1060
1061 return aPageSize;
1062 }
1063
appendSubPage(GDIMetaFile & o_rMtf,const tools::Rectangle & i_rClipRect,GDIMetaFile & io_rSubPage,bool i_bDrawBorder)1064 static void appendSubPage( GDIMetaFile& o_rMtf, const tools::Rectangle& i_rClipRect, GDIMetaFile& io_rSubPage, bool i_bDrawBorder )
1065 {
1066 // intersect all clipregion actions with our clip rect
1067 io_rSubPage.WindStart();
1068 io_rSubPage.Clip( i_rClipRect );
1069
1070 // save gstate
1071 o_rMtf.AddAction( new MetaPushAction( PushFlags::ALL ) );
1072
1073 // clip to page rect
1074 o_rMtf.AddAction( new MetaClipRegionAction( vcl::Region( i_rClipRect ), true ) );
1075
1076 // append the subpage
1077 io_rSubPage.WindStart();
1078 io_rSubPage.Play( o_rMtf );
1079
1080 // restore gstate
1081 o_rMtf.AddAction( new MetaPopAction() );
1082
1083 // draw a border
1084 if( !i_bDrawBorder )
1085 return;
1086
1087 // save gstate
1088 o_rMtf.AddAction( new MetaPushAction( PushFlags::LINECOLOR | PushFlags::FILLCOLOR | PushFlags::CLIPREGION | PushFlags::MAPMODE ) );
1089 o_rMtf.AddAction( new MetaMapModeAction( MapMode( MapUnit::Map100thMM ) ) );
1090
1091 tools::Rectangle aBorderRect( i_rClipRect );
1092 o_rMtf.AddAction( new MetaLineColorAction( COL_BLACK, true ) );
1093 o_rMtf.AddAction( new MetaFillColorAction( COL_TRANSPARENT, false ) );
1094 o_rMtf.AddAction( new MetaRectAction( aBorderRect ) );
1095
1096 // restore gstate
1097 o_rMtf.AddAction( new MetaPopAction() );
1098 }
1099
getFilteredPageFile(int i_nFilteredPage,GDIMetaFile & o_rMtf,bool i_bMayUseCache)1100 PrinterController::PageSize PrinterController::getFilteredPageFile( int i_nFilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
1101 {
1102 const MultiPageSetup& rMPS( mpImplData->maMultiPage );
1103 int nSubPages = rMPS.nRows * rMPS.nColumns;
1104 if( nSubPages < 1 )
1105 nSubPages = 1;
1106
1107 // reverse sheet order
1108 if( mpImplData->mbReversePageOrder )
1109 {
1110 int nDocPages = getFilteredPageCount();
1111 i_nFilteredPage = nDocPages - 1 - i_nFilteredPage;
1112 }
1113
1114 // there is no filtering to be done (and possibly the page size of the
1115 // original page is to be set), when N-Up is "neutral" that is there is
1116 // only one subpage and the margins are 0
1117 if( nSubPages == 1 &&
1118 rMPS.nLeftMargin == 0 && rMPS.nRightMargin == 0 &&
1119 rMPS.nTopMargin == 0 && rMPS.nBottomMargin == 0 )
1120 {
1121 PrinterController::PageSize aPageSize = getPageFile( i_nFilteredPage, o_rMtf, i_bMayUseCache );
1122 if (mpImplData->meJobState != css::view::PrintableState_JOB_STARTED)
1123 { // rhbz#657394: check that we are still printing...
1124 return PrinterController::PageSize();
1125 }
1126 Size aPaperSize = mpImplData->getRealPaperSize( aPageSize.aSize, true );
1127 mpImplData->mxPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
1128 mpImplData->mxPrinter->SetPaperSizeUser( aPaperSize );
1129 if( aPaperSize != aPageSize.aSize )
1130 {
1131 // user overridden page size, center Metafile
1132 o_rMtf.WindStart();
1133 tools::Long nDX = (aPaperSize.Width() - aPageSize.aSize.Width()) / 2;
1134 tools::Long nDY = (aPaperSize.Height() - aPageSize.aSize.Height()) / 2;
1135 o_rMtf.Move( nDX, nDY, mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() );
1136 o_rMtf.WindStart();
1137 o_rMtf.SetPrefSize( aPaperSize );
1138 aPageSize.aSize = aPaperSize;
1139 }
1140 return aPageSize;
1141 }
1142
1143 // set last page property really only on the very last page to be rendered
1144 // that is on the last subpage of a NUp run
1145 bool bIsLastPage = mpImplData->mbLastPage;
1146 mpImplData->mbLastPage = false;
1147
1148 Size aPaperSize( mpImplData->getRealPaperSize( mpImplData->maMultiPage.aPaperSize, false ) );
1149
1150 // multi page area: page size minus margins + one time spacing right and down
1151 // the added spacing is so each subpage can be calculated including its spacing
1152 Size aMPArea( aPaperSize );
1153 aMPArea.AdjustWidth( -(rMPS.nLeftMargin + rMPS.nRightMargin) );
1154 aMPArea.AdjustWidth(rMPS.nHorizontalSpacing );
1155 aMPArea.AdjustHeight( -(rMPS.nTopMargin + rMPS.nBottomMargin) );
1156 aMPArea.AdjustHeight(rMPS.nVerticalSpacing );
1157
1158 // determine offsets
1159 tools::Long nAdvX = aMPArea.Width() / rMPS.nColumns;
1160 tools::Long nAdvY = aMPArea.Height() / rMPS.nRows;
1161
1162 // determine size of a "cell" subpage, leave a little space around pages
1163 Size aSubPageSize( nAdvX - rMPS.nHorizontalSpacing, nAdvY - rMPS.nVerticalSpacing );
1164
1165 o_rMtf.Clear();
1166 o_rMtf.SetPrefSize( aPaperSize );
1167 o_rMtf.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
1168 o_rMtf.AddAction( new MetaMapModeAction( MapMode( MapUnit::Map100thMM ) ) );
1169
1170 int nDocPages = getPageCountProtected();
1171 if (mpImplData->meJobState != css::view::PrintableState_JOB_STARTED)
1172 { // rhbz#657394: check that we are still printing...
1173 return PrinterController::PageSize();
1174 }
1175 for( int nSubPage = 0; nSubPage < nSubPages; nSubPage++ )
1176 {
1177 // map current sub page to real page
1178 int nPage = i_nFilteredPage * nSubPages + nSubPage;
1179 if( nSubPage == nSubPages-1 ||
1180 nPage == nDocPages-1 )
1181 {
1182 mpImplData->mbLastPage = bIsLastPage;
1183 }
1184 if( nPage >= 0 && nPage < nDocPages )
1185 {
1186 GDIMetaFile aPageFile;
1187 PrinterController::PageSize aPageSize = getPageFile( nPage, aPageFile, i_bMayUseCache );
1188 if( aPageSize.aSize.Width() && aPageSize.aSize.Height() )
1189 {
1190 tools::Long nCellX = 0, nCellY = 0;
1191 switch( rMPS.nOrder )
1192 {
1193 case NupOrderType::LRTB:
1194 nCellX = (nSubPage % rMPS.nColumns);
1195 nCellY = (nSubPage / rMPS.nColumns);
1196 break;
1197 case NupOrderType::TBLR:
1198 nCellX = (nSubPage / rMPS.nRows);
1199 nCellY = (nSubPage % rMPS.nRows);
1200 break;
1201 case NupOrderType::RLTB:
1202 nCellX = rMPS.nColumns - 1 - (nSubPage % rMPS.nColumns);
1203 nCellY = (nSubPage / rMPS.nColumns);
1204 break;
1205 case NupOrderType::TBRL:
1206 nCellX = rMPS.nColumns - 1 - (nSubPage / rMPS.nRows);
1207 nCellY = (nSubPage % rMPS.nRows);
1208 break;
1209 }
1210 // scale the metafile down to a sub page size
1211 double fScaleX = double(aSubPageSize.Width())/double(aPageSize.aSize.Width());
1212 double fScaleY = double(aSubPageSize.Height())/double(aPageSize.aSize.Height());
1213 double fScale = std::min( fScaleX, fScaleY );
1214 aPageFile.Scale( fScale, fScale );
1215 aPageFile.WindStart();
1216
1217 // move the subpage so it is centered in its "cell"
1218 tools::Long nOffX = (aSubPageSize.Width() - tools::Long(double(aPageSize.aSize.Width()) * fScale)) / 2;
1219 tools::Long nOffY = (aSubPageSize.Height() - tools::Long(double(aPageSize.aSize.Height()) * fScale)) / 2;
1220 tools::Long nX = rMPS.nLeftMargin + nOffX + nAdvX * nCellX;
1221 tools::Long nY = rMPS.nTopMargin + nOffY + nAdvY * nCellY;
1222 aPageFile.Move( nX, nY, mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() );
1223 aPageFile.WindStart();
1224 // calculate border rectangle
1225 tools::Rectangle aSubPageRect( Point( nX, nY ),
1226 Size( tools::Long(double(aPageSize.aSize.Width())*fScale),
1227 tools::Long(double(aPageSize.aSize.Height())*fScale) ) );
1228
1229 // append subpage to page
1230 appendSubPage( o_rMtf, aSubPageRect, aPageFile, rMPS.bDrawBorder );
1231 }
1232 }
1233 }
1234 o_rMtf.WindStart();
1235
1236 // subsequent getPageFile calls have changed the paper, reset it to current value
1237 mpImplData->mxPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
1238 mpImplData->mxPrinter->SetPaperSizeUser( aPaperSize );
1239
1240 return PrinterController::PageSize( aPaperSize, true );
1241 }
1242
getFilteredPageCount() const1243 int PrinterController::getFilteredPageCount() const
1244 {
1245 int nDiv = mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns;
1246 if( nDiv < 1 )
1247 nDiv = 1;
1248 return (getPageCountProtected() + (nDiv-1)) / nDiv;
1249 }
1250
removeTransparencies(GDIMetaFile const & i_rIn,GDIMetaFile & o_rOut)1251 DrawModeFlags PrinterController::removeTransparencies( GDIMetaFile const & i_rIn, GDIMetaFile& o_rOut )
1252 {
1253 DrawModeFlags nRestoreDrawMode = mpImplData->mxPrinter->GetDrawMode();
1254 sal_Int32 nMaxBmpDPIX = mpImplData->mxPrinter->GetDPIX();
1255 sal_Int32 nMaxBmpDPIY = mpImplData->mxPrinter->GetDPIY();
1256
1257 const PrinterOptions& rPrinterOptions = mpImplData->mxPrinter->GetPrinterOptions();
1258
1259 static const sal_Int32 OPTIMAL_BMP_RESOLUTION = 300;
1260 static const sal_Int32 NORMAL_BMP_RESOLUTION = 200;
1261
1262 if( rPrinterOptions.IsReduceBitmaps() )
1263 {
1264 // calculate maximum resolution for bitmap graphics
1265 if( PrinterBitmapMode::Optimal == rPrinterOptions.GetReducedBitmapMode() )
1266 {
1267 nMaxBmpDPIX = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1268 nMaxBmpDPIY = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1269 }
1270 else if( PrinterBitmapMode::Normal == rPrinterOptions.GetReducedBitmapMode() )
1271 {
1272 nMaxBmpDPIX = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1273 nMaxBmpDPIY = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1274 }
1275 else
1276 {
1277 nMaxBmpDPIX = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIX );
1278 nMaxBmpDPIY = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIY );
1279 }
1280 }
1281
1282 // convert to greyscales
1283 if( rPrinterOptions.IsConvertToGreyscales() )
1284 {
1285 mpImplData->mxPrinter->SetDrawMode( mpImplData->mxPrinter->GetDrawMode() |
1286 ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
1287 DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
1288 }
1289
1290 // disable transparency output
1291 if( rPrinterOptions.IsReduceTransparency() && ( PrinterTransparencyMode::NONE == rPrinterOptions.GetReducedTransparencyMode() ) )
1292 {
1293 mpImplData->mxPrinter->SetDrawMode( mpImplData->mxPrinter->GetDrawMode() | DrawModeFlags::NoTransparency );
1294 }
1295
1296 Color aBg( COL_TRANSPARENT ); // default: let RemoveTransparenciesFromMetaFile do its own background logic
1297 if( mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns > 1 )
1298 {
1299 // in N-Up printing we have no "page" background operation
1300 // we also have no way to determine the paper color
1301 // so let's go for white, which will kill 99.9% of the real cases
1302 aBg = COL_WHITE;
1303 }
1304 mpImplData->mxPrinter->RemoveTransparenciesFromMetaFile( i_rIn, o_rOut, nMaxBmpDPIX, nMaxBmpDPIY,
1305 rPrinterOptions.IsReduceTransparency(),
1306 rPrinterOptions.GetReducedTransparencyMode() == PrinterTransparencyMode::Auto,
1307 rPrinterOptions.IsReduceBitmaps() && rPrinterOptions.IsReducedBitmapIncludesTransparency(),
1308 aBg
1309 );
1310 return nRestoreDrawMode;
1311 }
1312
printFilteredPage(int i_nPage)1313 void PrinterController::printFilteredPage( int i_nPage )
1314 {
1315 if( mpImplData->meJobState != css::view::PrintableState_JOB_STARTED )
1316 return; // rhbz#657394: check that we are still printing...
1317
1318 GDIMetaFile aPageFile;
1319 PrinterController::PageSize aPageSize = getFilteredPageFile( i_nPage, aPageFile );
1320
1321 if( mpImplData->mxProgress )
1322 {
1323 // do nothing if printing is canceled
1324 if( mpImplData->mxProgress->isCanceled() )
1325 {
1326 setJobState( css::view::PrintableState_JOB_ABORTED );
1327 return;
1328 }
1329 }
1330
1331 // in N-Up printing set the correct page size
1332 mpImplData->mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
1333 // aPageSize was filtered through mpImplData->getRealPaperSize already by getFilteredPageFile()
1334 mpImplData->mxPrinter->SetPaperSizeUser( aPageSize.aSize );
1335 if( mpImplData->mnFixedPaperBin != -1 &&
1336 mpImplData->mxPrinter->GetPaperBin() != mpImplData->mnFixedPaperBin )
1337 {
1338 mpImplData->mxPrinter->SetPaperBin( mpImplData->mnFixedPaperBin );
1339 }
1340
1341 // if full paper is meant to be used, move the output to accommodate for pageoffset
1342 if( aPageSize.bFullPaper )
1343 {
1344 Point aPageOffset( mpImplData->mxPrinter->GetPageOffset() );
1345 aPageFile.WindStart();
1346 aPageFile.Move( -aPageOffset.X(), -aPageOffset.Y(), mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() );
1347 }
1348
1349 GDIMetaFile aCleanedFile;
1350 DrawModeFlags nRestoreDrawMode = removeTransparencies( aPageFile, aCleanedFile );
1351
1352 mpImplData->mxPrinter->EnableOutput();
1353
1354 // actually print the page
1355 mpImplData->mxPrinter->ImplStartPage();
1356
1357 mpImplData->mxPrinter->Push();
1358 aCleanedFile.WindStart();
1359 aCleanedFile.Play(*mpImplData->mxPrinter);
1360 mpImplData->mxPrinter->Pop();
1361
1362 mpImplData->mxPrinter->ImplEndPage();
1363
1364 mpImplData->mxPrinter->SetDrawMode( nRestoreDrawMode );
1365 }
1366
jobStarted()1367 void PrinterController::jobStarted()
1368 {
1369 }
1370
jobFinished(css::view::PrintableState)1371 void PrinterController::jobFinished( css::view::PrintableState )
1372 {
1373 }
1374
abortJob()1375 void PrinterController::abortJob()
1376 {
1377 setJobState( css::view::PrintableState_JOB_ABORTED );
1378 // applications (well, sw) depend on a page request with "IsLastPage" = true
1379 // to free resources, else they (well, sw) will crash eventually
1380 setLastPage( true );
1381
1382 if (mpImplData->mxProgress)
1383 {
1384 mpImplData->mxProgress->response(RET_CANCEL);
1385 mpImplData->mxProgress.reset();
1386 }
1387
1388 GDIMetaFile aMtf;
1389 getPageFile( 0, aMtf );
1390 }
1391
setLastPage(bool i_bLastPage)1392 void PrinterController::setLastPage( bool i_bLastPage )
1393 {
1394 mpImplData->mbLastPage = i_bLastPage;
1395 }
1396
setReversePrint(bool i_bReverse)1397 void PrinterController::setReversePrint( bool i_bReverse )
1398 {
1399 mpImplData->mbReversePageOrder = i_bReverse;
1400 }
1401
setPapersizeFromSetup(bool i_bPapersizeFromSetup)1402 void PrinterController::setPapersizeFromSetup( bool i_bPapersizeFromSetup )
1403 {
1404 mpImplData->mbPapersizeFromSetup = i_bPapersizeFromSetup;
1405 mpImplData->mxPrinter->SetPrinterSettingsPreferred( i_bPapersizeFromSetup );
1406 if ( i_bPapersizeFromSetup )
1407 mpImplData->mbPapersizeFromUser = !i_bPapersizeFromSetup;
1408 }
1409
getPapersizeFromSetup() const1410 bool PrinterController::getPapersizeFromSetup() const
1411 {
1412 return mpImplData->mbPapersizeFromSetup;
1413 }
1414
getPaperSizeSetup() const1415 Size& PrinterController::getPaperSizeSetup() const
1416 {
1417 return mpImplData->maDefaultPageSize;
1418 }
1419
setPaperSizeFromUser(Size i_aUserSize)1420 void PrinterController::setPaperSizeFromUser( Size i_aUserSize )
1421 {
1422 mpImplData->mbPapersizeFromUser = true;
1423 mpImplData->mbPapersizeFromSetup = false;
1424 mpImplData->mxPrinter->SetPrinterSettingsPreferred( false );
1425
1426 mpImplData->maUserPageSize = i_aUserSize;
1427 }
1428
getPaperSizeFromUser() const1429 Size& PrinterController::getPaperSizeFromUser() const
1430 {
1431 return mpImplData->maUserPageSize;
1432 }
1433
isPaperSizeFromUser() const1434 bool PrinterController::isPaperSizeFromUser() const
1435 {
1436 return mpImplData->mbPapersizeFromUser;
1437 }
1438
setPrinterModified(bool i_bPrinterModified)1439 void PrinterController::setPrinterModified( bool i_bPrinterModified )
1440 {
1441 mpImplData->mbPrinterModified = i_bPrinterModified;
1442 }
1443
getPrinterModified() const1444 bool PrinterController::getPrinterModified() const
1445 {
1446 return mpImplData->mbPrinterModified;
1447 }
1448
getJobProperties(const css::uno::Sequence<css::beans::PropertyValue> & i_rMergeList) const1449 css::uno::Sequence< css::beans::PropertyValue > PrinterController::getJobProperties( const css::uno::Sequence< css::beans::PropertyValue >& i_rMergeList ) const
1450 {
1451 std::unordered_set< OUString > aMergeSet;
1452 size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3;
1453 for( const auto& rPropVal : i_rMergeList )
1454 aMergeSet.insert( rPropVal.Name );
1455
1456 css::uno::Sequence< css::beans::PropertyValue > aResult( nResultLen );
1457 std::copy(i_rMergeList.begin(), i_rMergeList.end(), aResult.begin());
1458 int nCur = i_rMergeList.getLength();
1459 for(const css::beans::PropertyValue & rPropVal : mpImplData->maUIProperties)
1460 {
1461 if( aMergeSet.find( rPropVal.Name ) == aMergeSet.end() )
1462 aResult[nCur++] = rPropVal;
1463 }
1464 // append IsFirstPage
1465 if( aMergeSet.find( "IsFirstPage" ) == aMergeSet.end() )
1466 {
1467 css::beans::PropertyValue aVal;
1468 aVal.Name = "IsFirstPage";
1469 aVal.Value <<= mpImplData->mbFirstPage;
1470 aResult[nCur++] = aVal;
1471 }
1472 // append IsLastPage
1473 if( aMergeSet.find( "IsLastPage" ) == aMergeSet.end() )
1474 {
1475 css::beans::PropertyValue aVal;
1476 aVal.Name = "IsLastPage";
1477 aVal.Value <<= mpImplData->mbLastPage;
1478 aResult[nCur++] = aVal;
1479 }
1480 // append IsPrinter
1481 if( aMergeSet.find( "IsPrinter" ) == aMergeSet.end() )
1482 {
1483 css::beans::PropertyValue aVal;
1484 aVal.Name = "IsPrinter";
1485 aVal.Value <<= true;
1486 aResult[nCur++] = aVal;
1487 }
1488 aResult.realloc( nCur );
1489 return aResult;
1490 }
1491
getUIOptions() const1492 const css::uno::Sequence< css::beans::PropertyValue >& PrinterController::getUIOptions() const
1493 {
1494 return mpImplData->maUIOptions;
1495 }
1496
getValue(const OUString & i_rProperty)1497 css::beans::PropertyValue* PrinterController::getValue( const OUString& i_rProperty )
1498 {
1499 std::unordered_map< OUString, size_t >::const_iterator it =
1500 mpImplData->maPropertyToIndex.find( i_rProperty );
1501 return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : nullptr;
1502 }
1503
getValue(const OUString & i_rProperty) const1504 const css::beans::PropertyValue* PrinterController::getValue( const OUString& i_rProperty ) const
1505 {
1506 std::unordered_map< OUString, size_t >::const_iterator it =
1507 mpImplData->maPropertyToIndex.find( i_rProperty );
1508 return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : nullptr;
1509 }
1510
setValue(const OUString & i_rPropertyName,const css::uno::Any & i_rValue)1511 void PrinterController::setValue( const OUString& i_rPropertyName, const css::uno::Any& i_rValue )
1512 {
1513 css::beans::PropertyValue aVal;
1514 aVal.Name = i_rPropertyName;
1515 aVal.Value = i_rValue;
1516
1517 setValue( aVal );
1518 }
1519
setValue(const css::beans::PropertyValue & i_rPropertyValue)1520 void PrinterController::setValue( const css::beans::PropertyValue& i_rPropertyValue )
1521 {
1522 std::unordered_map< OUString, size_t >::const_iterator it =
1523 mpImplData->maPropertyToIndex.find( i_rPropertyValue.Name );
1524 if( it != mpImplData->maPropertyToIndex.end() )
1525 mpImplData->maUIProperties[ it->second ] = i_rPropertyValue;
1526 else
1527 {
1528 // insert correct index into property map
1529 mpImplData->maPropertyToIndex[ i_rPropertyValue.Name ] = mpImplData->maUIProperties.size();
1530 mpImplData->maUIProperties.push_back( i_rPropertyValue );
1531 mpImplData->maUIPropertyEnabled.push_back( true );
1532 }
1533 }
1534
setUIOptions(const css::uno::Sequence<css::beans::PropertyValue> & i_rOptions)1535 void PrinterController::setUIOptions( const css::uno::Sequence< css::beans::PropertyValue >& i_rOptions )
1536 {
1537 SAL_WARN_IF( mpImplData->maUIOptions.hasElements(), "vcl.gdi", "setUIOptions called twice !" );
1538
1539 mpImplData->maUIOptions = i_rOptions;
1540
1541 for( const auto& rOpt : i_rOptions )
1542 {
1543 css::uno::Sequence< css::beans::PropertyValue > aOptProp;
1544 rOpt.Value >>= aOptProp;
1545 bool bIsEnabled = true;
1546 bool bHaveProperty = false;
1547 OUString aPropName;
1548 vcl::ImplPrinterControllerData::ControlDependency aDep;
1549 css::uno::Sequence< sal_Bool > aChoicesDisabled;
1550 for( const css::beans::PropertyValue& rEntry : std::as_const(aOptProp) )
1551 {
1552 if ( rEntry.Name == "Property" )
1553 {
1554 css::beans::PropertyValue aVal;
1555 rEntry.Value >>= aVal;
1556 DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name )
1557 == mpImplData->maPropertyToIndex.end(), "duplicate property entry" );
1558 setValue( aVal );
1559 aPropName = aVal.Name;
1560 bHaveProperty = true;
1561 }
1562 else if ( rEntry.Name == "Enabled" )
1563 {
1564 bool bValue = true;
1565 rEntry.Value >>= bValue;
1566 bIsEnabled = bValue;
1567 }
1568 else if ( rEntry.Name == "DependsOnName" )
1569 {
1570 rEntry.Value >>= aDep.maDependsOnName;
1571 }
1572 else if ( rEntry.Name == "DependsOnEntry" )
1573 {
1574 rEntry.Value >>= aDep.mnDependsOnEntry;
1575 }
1576 else if ( rEntry.Name == "ChoicesDisabled" )
1577 {
1578 rEntry.Value >>= aChoicesDisabled;
1579 }
1580 }
1581 if( bHaveProperty )
1582 {
1583 vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it =
1584 mpImplData->maPropertyToIndex.find( aPropName );
1585 // sanity check
1586 if( it != mpImplData->maPropertyToIndex.end() )
1587 {
1588 mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled;
1589 }
1590 if( !aDep.maDependsOnName.isEmpty() )
1591 mpImplData->maControlDependencies[ aPropName ] = aDep;
1592 if( aChoicesDisabled.hasElements() )
1593 mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled;
1594 }
1595 }
1596 }
1597
isUIOptionEnabled(const OUString & i_rProperty) const1598 bool PrinterController::isUIOptionEnabled( const OUString& i_rProperty ) const
1599 {
1600 bool bEnabled = false;
1601 std::unordered_map< OUString, size_t >::const_iterator prop_it =
1602 mpImplData->maPropertyToIndex.find( i_rProperty );
1603 if( prop_it != mpImplData->maPropertyToIndex.end() )
1604 {
1605 bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second];
1606
1607 if( bEnabled )
1608 {
1609 // check control dependencies
1610 vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1611 mpImplData->maControlDependencies.find( i_rProperty );
1612 if( it != mpImplData->maControlDependencies.end() )
1613 {
1614 // check if the dependency is enabled
1615 // if the dependency is disabled, we are too
1616 bEnabled = isUIOptionEnabled( it->second.maDependsOnName );
1617
1618 if( bEnabled )
1619 {
1620 // does the dependency have the correct value ?
1621 const css::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName );
1622 OSL_ENSURE( pVal, "unknown property in dependency" );
1623 if( pVal )
1624 {
1625 sal_Int32 nDepVal = 0;
1626 bool bDepVal = false;
1627 if( pVal->Value >>= nDepVal )
1628 {
1629 bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1);
1630 }
1631 else if( pVal->Value >>= bDepVal )
1632 {
1633 // could be a dependency on a checked boolean
1634 // in this case the dependency is on a non zero for checked value
1635 bEnabled = ( bDepVal && it->second.mnDependsOnEntry != 0) ||
1636 ( ! bDepVal && it->second.mnDependsOnEntry == 0);
1637 }
1638 else
1639 {
1640 // if the type does not match something is awry
1641 OSL_FAIL( "strange type in control dependency" );
1642 bEnabled = false;
1643 }
1644 }
1645 }
1646 }
1647 }
1648 }
1649 return bEnabled;
1650 }
1651
isUIChoiceEnabled(const OUString & i_rProperty,sal_Int32 i_nValue) const1652 bool PrinterController::isUIChoiceEnabled( const OUString& i_rProperty, sal_Int32 i_nValue ) const
1653 {
1654 bool bEnabled = true;
1655 ImplPrinterControllerData::ChoiceDisableMap::const_iterator it =
1656 mpImplData->maChoiceDisableMap.find( i_rProperty );
1657 if(it != mpImplData->maChoiceDisableMap.end() )
1658 {
1659 const css::uno::Sequence< sal_Bool >& rDisabled( it->second );
1660 if( i_nValue >= 0 && i_nValue < rDisabled.getLength() )
1661 bEnabled = ! rDisabled[i_nValue];
1662 }
1663 return bEnabled;
1664 }
1665
makeEnabled(const OUString & i_rProperty)1666 OUString PrinterController::makeEnabled( const OUString& i_rProperty )
1667 {
1668 OUString aDependency;
1669
1670 vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1671 mpImplData->maControlDependencies.find( i_rProperty );
1672 if( it != mpImplData->maControlDependencies.end() )
1673 {
1674 if( isUIOptionEnabled( it->second.maDependsOnName ) )
1675 {
1676 aDependency = it->second.maDependsOnName;
1677 const css::beans::PropertyValue* pVal = getValue( aDependency );
1678 OSL_ENSURE( pVal, "unknown property in dependency" );
1679 if( pVal )
1680 {
1681 sal_Int32 nDepVal = 0;
1682 bool bDepVal = false;
1683 if( pVal->Value >>= nDepVal )
1684 {
1685 if( it->second.mnDependsOnEntry != -1 )
1686 {
1687 setValue( aDependency, css::uno::makeAny( sal_Int32( it->second.mnDependsOnEntry ) ) );
1688 }
1689 }
1690 else if( pVal->Value >>= bDepVal )
1691 {
1692 setValue( aDependency, css::uno::makeAny( it->second.mnDependsOnEntry != 0 ) );
1693 }
1694 else
1695 {
1696 // if the type does not match something is awry
1697 OSL_FAIL( "strange type in control dependency" );
1698 }
1699 }
1700 }
1701 }
1702
1703 return aDependency;
1704 }
1705
createProgressDialog()1706 void PrinterController::createProgressDialog()
1707 {
1708 if (!mpImplData->mxProgress)
1709 {
1710 bool bShow = true;
1711 css::beans::PropertyValue* pMonitor = getValue( "MonitorVisible" );
1712 if( pMonitor )
1713 pMonitor->Value >>= bShow;
1714 else
1715 {
1716 const css::beans::PropertyValue* pVal = getValue( "IsApi" );
1717 if( pVal )
1718 {
1719 bool bApi = false;
1720 pVal->Value >>= bApi;
1721 bShow = ! bApi;
1722 }
1723 }
1724
1725 if( bShow && ! Application::IsHeadlessModeEnabled() )
1726 {
1727 mpImplData->mxProgress = std::make_shared<PrintProgressDialog>(getWindow(), getPageCountProtected());
1728 weld::DialogController::runAsync(mpImplData->mxProgress, [](sal_Int32 /*nResult*/){});
1729 }
1730 }
1731 else
1732 {
1733 mpImplData->mxProgress->response(RET_CANCEL);
1734 mpImplData->mxProgress.reset();
1735 }
1736 }
1737
isProgressCanceled() const1738 bool PrinterController::isProgressCanceled() const
1739 {
1740 return mpImplData->mxProgress && mpImplData->mxProgress->isCanceled();
1741 }
1742
setMultipage(const MultiPageSetup & i_rMPS)1743 void PrinterController::setMultipage( const MultiPageSetup& i_rMPS )
1744 {
1745 mpImplData->maMultiPage = i_rMPS;
1746 }
1747
getMultipage() const1748 const PrinterController::MultiPageSetup& PrinterController::getMultipage() const
1749 {
1750 return mpImplData->maMultiPage;
1751 }
1752
resetPaperToLastConfigured()1753 void PrinterController::resetPaperToLastConfigured()
1754 {
1755 mpImplData->resetPaperToLastConfigured();
1756 }
1757
pushPropertiesToPrinter()1758 void PrinterController::pushPropertiesToPrinter()
1759 {
1760 sal_Int32 nCopyCount = 1;
1761 // set copycount and collate
1762 const css::beans::PropertyValue* pVal = getValue( "CopyCount" );
1763 if( pVal )
1764 pVal->Value >>= nCopyCount;
1765 bool bCollate = false;
1766 pVal = getValue( "Collate" );
1767 if( pVal )
1768 pVal->Value >>= bCollate;
1769 mpImplData->mxPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate );
1770
1771 pVal = getValue("SinglePrintJobs");
1772 bool bSinglePrintJobs = false;
1773 if (pVal)
1774 pVal->Value >>= bSinglePrintJobs;
1775 mpImplData->mxPrinter->SetSinglePrintJobs(bSinglePrintJobs);
1776
1777 // duplex mode
1778 pVal = getValue( "DuplexMode" );
1779 if( pVal )
1780 {
1781 sal_Int16 nDuplex = css::view::DuplexMode::UNKNOWN;
1782 pVal->Value >>= nDuplex;
1783 switch( nDuplex )
1784 {
1785 case css::view::DuplexMode::OFF: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::Off ); break;
1786 case css::view::DuplexMode::LONGEDGE: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::LongEdge ); break;
1787 case css::view::DuplexMode::SHORTEDGE: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::ShortEdge ); break;
1788 }
1789 }
1790 }
1791
isShowDialogs() const1792 bool PrinterController::isShowDialogs() const
1793 {
1794 bool bApi = getBoolProperty( "IsApi", false );
1795 return ! bApi && ! Application::IsHeadlessModeEnabled();
1796 }
1797
isDirectPrint() const1798 bool PrinterController::isDirectPrint() const
1799 {
1800 bool bDirect = getBoolProperty( "IsDirect", false );
1801 return bDirect;
1802 }
1803
getBoolProperty(const OUString & i_rProperty,bool i_bFallback) const1804 bool PrinterController::getBoolProperty( const OUString& i_rProperty, bool i_bFallback ) const
1805 {
1806 bool bRet = i_bFallback;
1807 const css::beans::PropertyValue* pVal = getValue( i_rProperty );
1808 if( pVal )
1809 pVal->Value >>= bRet;
1810 return bRet;
1811 }
1812
getIntProperty(const OUString & i_rProperty,sal_Int32 i_nFallback) const1813 sal_Int32 PrinterController::getIntProperty( const OUString& i_rProperty, sal_Int32 i_nFallback ) const
1814 {
1815 sal_Int32 nRet = i_nFallback;
1816 const css::beans::PropertyValue* pVal = getValue( i_rProperty );
1817 if( pVal )
1818 pVal->Value >>= nRet;
1819 return nRet;
1820 }
1821
1822 /*
1823 * PrinterOptionsHelper
1824 **/
getValue(const OUString & i_rPropertyName) const1825 css::uno::Any PrinterOptionsHelper::getValue( const OUString& i_rPropertyName ) const
1826 {
1827 css::uno::Any aRet;
1828 std::unordered_map< OUString, css::uno::Any >::const_iterator it =
1829 m_aPropertyMap.find( i_rPropertyName );
1830 if( it != m_aPropertyMap.end() )
1831 aRet = it->second;
1832 return aRet;
1833 }
1834
getBoolValue(const OUString & i_rPropertyName,bool i_bDefault) const1835 bool PrinterOptionsHelper::getBoolValue( const OUString& i_rPropertyName, bool i_bDefault ) const
1836 {
1837 bool bRet = false;
1838 css::uno::Any aVal( getValue( i_rPropertyName ) );
1839 return (aVal >>= bRet) ? bRet : i_bDefault;
1840 }
1841
getIntValue(const OUString & i_rPropertyName,sal_Int64 i_nDefault) const1842 sal_Int64 PrinterOptionsHelper::getIntValue( const OUString& i_rPropertyName, sal_Int64 i_nDefault ) const
1843 {
1844 sal_Int64 nRet = 0;
1845 css::uno::Any aVal( getValue( i_rPropertyName ) );
1846 return (aVal >>= nRet) ? nRet : i_nDefault;
1847 }
1848
getStringValue(const OUString & i_rPropertyName) const1849 OUString PrinterOptionsHelper::getStringValue( const OUString& i_rPropertyName ) const
1850 {
1851 OUString aRet;
1852 css::uno::Any aVal( getValue( i_rPropertyName ) );
1853 return (aVal >>= aRet) ? aRet : OUString();
1854 }
1855
processProperties(const css::uno::Sequence<css::beans::PropertyValue> & i_rNewProp)1856 bool PrinterOptionsHelper::processProperties( const css::uno::Sequence< css::beans::PropertyValue >& i_rNewProp )
1857 {
1858 bool bChanged = false;
1859
1860 for( const auto& rVal : i_rNewProp )
1861 {
1862 std::unordered_map< OUString, css::uno::Any >::iterator it =
1863 m_aPropertyMap.find( rVal.Name );
1864
1865 bool bElementChanged = (it == m_aPropertyMap.end()) || (it->second != rVal.Value);
1866 if( bElementChanged )
1867 {
1868 m_aPropertyMap[ rVal.Name ] = rVal.Value;
1869 bChanged = true;
1870 }
1871 }
1872 return bChanged;
1873 }
1874
appendPrintUIOptions(css::uno::Sequence<css::beans::PropertyValue> & io_rProps) const1875 void PrinterOptionsHelper::appendPrintUIOptions( css::uno::Sequence< css::beans::PropertyValue >& io_rProps ) const
1876 {
1877 if( !m_aUIProperties.empty() )
1878 {
1879 sal_Int32 nIndex = io_rProps.getLength();
1880 io_rProps.realloc( nIndex+1 );
1881 css::beans::PropertyValue aVal;
1882 aVal.Name = "ExtraPrintUIOptions";
1883 aVal.Value <<= comphelper::containerToSequence(m_aUIProperties);
1884 io_rProps[ nIndex ] = aVal;
1885 }
1886 }
1887
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)1888 css::uno::Any PrinterOptionsHelper::setUIControlOpt(const css::uno::Sequence< OUString >& i_rIDs,
1889 const OUString& i_rTitle,
1890 const css::uno::Sequence< OUString >& i_rHelpIds,
1891 const OUString& i_rType,
1892 const css::beans::PropertyValue* i_pVal,
1893 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
1894 {
1895 sal_Int32 nElements =
1896 2 // ControlType + ID
1897 + (i_rTitle.isEmpty() ? 0 : 1) // Text
1898 + (i_rHelpIds.hasElements() ? 1 : 0) // HelpId
1899 + (i_pVal ? 1 : 0) // Property
1900 + i_rControlOptions.maAddProps.size() // additional props
1901 + (i_rControlOptions.maGroupHint.isEmpty() ? 0 : 1) // grouping
1902 + (i_rControlOptions.mbInternalOnly ? 1 : 0) // internal hint
1903 + (i_rControlOptions.mbEnabled ? 0 : 1) // enabled
1904 ;
1905 if( !i_rControlOptions.maDependsOnName.isEmpty() )
1906 {
1907 nElements += 1;
1908 if( i_rControlOptions.mnDependsOnEntry != -1 )
1909 nElements += 1;
1910 if( i_rControlOptions.mbAttachToDependency )
1911 nElements += 1;
1912 }
1913
1914 css::uno::Sequence< css::beans::PropertyValue > aCtrl( nElements );
1915 sal_Int32 nUsed = 0;
1916 if( !i_rTitle.isEmpty() )
1917 {
1918 aCtrl[nUsed ].Name = "Text";
1919 aCtrl[nUsed++].Value <<= i_rTitle;
1920 }
1921 if( i_rHelpIds.hasElements() )
1922 {
1923 aCtrl[nUsed ].Name = "HelpId";
1924 aCtrl[nUsed++].Value <<= i_rHelpIds;
1925 }
1926 aCtrl[nUsed ].Name = "ControlType";
1927 aCtrl[nUsed++].Value <<= i_rType;
1928 aCtrl[nUsed ].Name = "ID";
1929 aCtrl[nUsed++].Value <<= i_rIDs;
1930 if( i_pVal )
1931 {
1932 aCtrl[nUsed ].Name = "Property";
1933 aCtrl[nUsed++].Value <<= *i_pVal;
1934 }
1935 if( !i_rControlOptions.maDependsOnName.isEmpty() )
1936 {
1937 aCtrl[nUsed ].Name = "DependsOnName";
1938 aCtrl[nUsed++].Value <<= i_rControlOptions.maDependsOnName;
1939 if( i_rControlOptions.mnDependsOnEntry != -1 )
1940 {
1941 aCtrl[nUsed ].Name = "DependsOnEntry";
1942 aCtrl[nUsed++].Value <<= i_rControlOptions.mnDependsOnEntry;
1943 }
1944 if( i_rControlOptions.mbAttachToDependency )
1945 {
1946 aCtrl[nUsed ].Name = "AttachToDependency";
1947 aCtrl[nUsed++].Value <<= i_rControlOptions.mbAttachToDependency;
1948 }
1949 }
1950 if( !i_rControlOptions.maGroupHint.isEmpty() )
1951 {
1952 aCtrl[nUsed ].Name = "GroupingHint";
1953 aCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint;
1954 }
1955 if( i_rControlOptions.mbInternalOnly )
1956 {
1957 aCtrl[nUsed ].Name = "InternalUIOnly";
1958 aCtrl[nUsed++].Value <<= true;
1959 }
1960 if( ! i_rControlOptions.mbEnabled )
1961 {
1962 aCtrl[nUsed ].Name = "Enabled";
1963 aCtrl[nUsed++].Value <<= false;
1964 }
1965
1966 sal_Int32 nAddProps = i_rControlOptions.maAddProps.size();
1967 for( sal_Int32 i = 0; i < nAddProps; i++ )
1968 aCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i];
1969
1970 SAL_WARN_IF( nUsed != nElements, "vcl.gdi", "nUsed != nElements, probable heap corruption" );
1971
1972 return css::uno::makeAny( aCtrl );
1973 }
1974
setGroupControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId)1975 css::uno::Any PrinterOptionsHelper::setGroupControlOpt(const OUString& i_rID,
1976 const OUString& i_rTitle,
1977 const OUString& i_rHelpId)
1978 {
1979 css::uno::Sequence< OUString > aHelpId;
1980 if( !i_rHelpId.isEmpty() )
1981 {
1982 aHelpId.realloc( 1 );
1983 *aHelpId.getArray() = i_rHelpId;
1984 }
1985 css::uno::Sequence< OUString > aIds { i_rID };
1986 return setUIControlOpt(aIds, i_rTitle, aHelpId, "Group");
1987 }
1988
setSubgroupControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)1989 css::uno::Any PrinterOptionsHelper::setSubgroupControlOpt(const OUString& i_rID,
1990 const OUString& i_rTitle,
1991 const OUString& i_rHelpId,
1992 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
1993 {
1994 css::uno::Sequence< OUString > aHelpId;
1995 if( !i_rHelpId.isEmpty() )
1996 {
1997 aHelpId.realloc( 1 );
1998 *aHelpId.getArray() = i_rHelpId;
1999 }
2000 css::uno::Sequence< OUString > aIds { i_rID };
2001 return setUIControlOpt(aIds, i_rTitle, aHelpId, "Subgroup", nullptr, i_rControlOptions);
2002 }
2003
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)2004 css::uno::Any PrinterOptionsHelper::setBoolControlOpt(const OUString& i_rID,
2005 const OUString& i_rTitle,
2006 const OUString& i_rHelpId,
2007 const OUString& i_rProperty,
2008 bool i_bValue,
2009 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2010 {
2011 css::uno::Sequence< OUString > aHelpId;
2012 if( !i_rHelpId.isEmpty() )
2013 {
2014 aHelpId.realloc( 1 );
2015 *aHelpId.getArray() = i_rHelpId;
2016 }
2017 css::beans::PropertyValue aVal;
2018 aVal.Name = i_rProperty;
2019 aVal.Value <<= i_bValue;
2020 css::uno::Sequence< OUString > aIds { i_rID };
2021 return setUIControlOpt(aIds, i_rTitle, aHelpId, "Bool", &aVal, i_rControlOptions);
2022 }
2023
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)2024 css::uno::Any PrinterOptionsHelper::setChoiceRadiosControlOpt(const css::uno::Sequence< OUString >& i_rIDs,
2025 const OUString& i_rTitle,
2026 const css::uno::Sequence< OUString >& i_rHelpId,
2027 const OUString& i_rProperty,
2028 const css::uno::Sequence< OUString >& i_rChoices,
2029 sal_Int32 i_nValue,
2030 const css::uno::Sequence< sal_Bool >& i_rDisabledChoices,
2031 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2032 {
2033 UIControlOptions aOpt( i_rControlOptions );
2034 sal_Int32 nUsed = aOpt.maAddProps.size();
2035 aOpt.maAddProps.resize( nUsed + 1 + (i_rDisabledChoices.hasElements() ? 1 : 0) );
2036 aOpt.maAddProps[nUsed].Name = "Choices";
2037 aOpt.maAddProps[nUsed].Value <<= i_rChoices;
2038 if( i_rDisabledChoices.hasElements() )
2039 {
2040 aOpt.maAddProps[nUsed+1].Name = "ChoicesDisabled";
2041 aOpt.maAddProps[nUsed+1].Value <<= i_rDisabledChoices;
2042 }
2043
2044 css::beans::PropertyValue aVal;
2045 aVal.Name = i_rProperty;
2046 aVal.Value <<= i_nValue;
2047 return setUIControlOpt(i_rIDs, i_rTitle, i_rHelpId, "Radio", &aVal, aOpt);
2048 }
2049
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)2050 css::uno::Any PrinterOptionsHelper::setChoiceListControlOpt(const OUString& i_rID,
2051 const OUString& i_rTitle,
2052 const css::uno::Sequence< OUString >& i_rHelpId,
2053 const OUString& i_rProperty,
2054 const css::uno::Sequence< OUString >& i_rChoices,
2055 sal_Int32 i_nValue,
2056 const css::uno::Sequence< sal_Bool >& i_rDisabledChoices,
2057 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2058 {
2059 UIControlOptions aOpt( i_rControlOptions );
2060 sal_Int32 nUsed = aOpt.maAddProps.size();
2061 aOpt.maAddProps.resize( nUsed + 1 + (i_rDisabledChoices.hasElements() ? 1 : 0) );
2062 aOpt.maAddProps[nUsed].Name = "Choices";
2063 aOpt.maAddProps[nUsed].Value <<= i_rChoices;
2064 if( i_rDisabledChoices.hasElements() )
2065 {
2066 aOpt.maAddProps[nUsed+1].Name = "ChoicesDisabled";
2067 aOpt.maAddProps[nUsed+1].Value <<= i_rDisabledChoices;
2068 }
2069
2070 css::beans::PropertyValue aVal;
2071 aVal.Name = i_rProperty;
2072 aVal.Value <<= i_nValue;
2073 css::uno::Sequence< OUString > aIds { i_rID };
2074 return setUIControlOpt(aIds, i_rTitle, i_rHelpId, "List", &aVal, aOpt);
2075 }
2076
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)2077 css::uno::Any PrinterOptionsHelper::setRangeControlOpt(const OUString& i_rID,
2078 const OUString& i_rTitle,
2079 const OUString& i_rHelpId,
2080 const OUString& i_rProperty,
2081 sal_Int32 i_nValue,
2082 sal_Int32 i_nMinValue,
2083 sal_Int32 i_nMaxValue,
2084 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2085 {
2086 UIControlOptions aOpt( i_rControlOptions );
2087 if( i_nMaxValue >= i_nMinValue )
2088 {
2089 sal_Int32 nUsed = aOpt.maAddProps.size();
2090 aOpt.maAddProps.resize( nUsed + 2 );
2091 aOpt.maAddProps[nUsed ].Name = "MinValue";
2092 aOpt.maAddProps[nUsed++].Value <<= i_nMinValue;
2093 aOpt.maAddProps[nUsed ].Name = "MaxValue";
2094 aOpt.maAddProps[nUsed++].Value <<= i_nMaxValue;
2095 }
2096
2097 css::uno::Sequence< OUString > aHelpId;
2098 if( !i_rHelpId.isEmpty() )
2099 {
2100 aHelpId.realloc( 1 );
2101 *aHelpId.getArray() = i_rHelpId;
2102 }
2103 css::beans::PropertyValue aVal;
2104 aVal.Name = i_rProperty;
2105 aVal.Value <<= i_nValue;
2106 css::uno::Sequence< OUString > aIds { i_rID };
2107 return setUIControlOpt(aIds, i_rTitle, aHelpId, "Range", &aVal, aOpt);
2108 }
2109
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)2110 css::uno::Any PrinterOptionsHelper::setEditControlOpt(const OUString& i_rID,
2111 const OUString& i_rTitle,
2112 const OUString& i_rHelpId,
2113 const OUString& i_rProperty,
2114 const OUString& i_rValue,
2115 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2116 {
2117 css::uno::Sequence< OUString > aHelpId;
2118 if( !i_rHelpId.isEmpty() )
2119 {
2120 aHelpId.realloc( 1 );
2121 *aHelpId.getArray() = i_rHelpId;
2122 }
2123 css::beans::PropertyValue aVal;
2124 aVal.Name = i_rProperty;
2125 aVal.Value <<= i_rValue;
2126 css::uno::Sequence< OUString > aIds { i_rID };
2127 return setUIControlOpt(aIds, i_rTitle, aHelpId, "Edit", &aVal, i_rControlOptions);
2128 }
2129
2130 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2131