Coverage Report

Created: 2026-11-06 11:29

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