Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/window/printdlg.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <o3tl/safeint.hxx>
21
#include <sal/log.hxx>
22
#include <osl/diagnose.h>
23
#include <rtl/ustrbuf.hxx>
24
#include <tools/mapunit.hxx>
25
#include <unotools/localedatawrapper.hxx>
26
#include <officecfg/Office/Common.hxx>
27
#include <officecfg/VCL.hxx>
28
29
#include <utility>
30
#include <vcl/QueueInfo.hxx>
31
#include <vcl/commandevent.hxx>
32
#include <vcl/decoview.hxx>
33
#include <vcl/help.hxx>
34
#include <vcl/naturalsort.hxx>
35
#include <vcl/print.hxx>
36
#include <vcl/printer/Options.hxx>
37
#include <vcl/PrinterSupport.hxx>
38
#include <vcl/rendercontext/DrawModeFlags.hxx>
39
#include <vcl/settings.hxx>
40
#include <vcl/svapp.hxx>
41
#include <vcl/virdev.hxx>
42
#include <vcl/wall.hxx>
43
#include <vcl/weld/Builder.hxx>
44
#include <vcl/weld/Dialog.hxx>
45
#include <vcl/weld/weldutils.hxx>
46
#include <vcl/windowstate.hxx>
47
48
#include <bitmaps.hlst>
49
#include <printdlg.hxx>
50
#include <strings.hrc>
51
#include <svdata.hxx>
52
53
#include <com/sun/star/beans/PropertyValue.hpp>
54
55
using namespace vcl;
56
using namespace com::sun::star;
57
using namespace com::sun::star::uno;
58
using namespace com::sun::star::container;
59
using namespace com::sun::star::beans;
60
61
enum
62
{
63
    ORIENTATION_AUTOMATIC,
64
    ORIENTATION_PORTRAIT,
65
    ORIENTATION_LANDSCAPE
66
};
67
68
namespace {
69
   bool lcl_ListBoxCompare( const OUString& rStr1, const OUString& rStr2 )
70
0
   {
71
0
       return vcl::NaturalSortCompare( rStr1, rStr2 ) < 0;
72
0
   }
73
}
74
75
PrintDialog::PrintPreviewWindow::PrintPreviewWindow(PrintDialog* pDialog)
76
0
    : mpDialog(pDialog)
77
0
    , maOrigSize( 10, 10 )
78
0
    , mnDPIX(Application::GetDefaultDevice()->GetDPIX())
79
0
    , mnDPIY(Application::GetDefaultDevice()->GetDPIY())
80
0
    , mbGreyscale( false )
81
0
{
82
0
}
83
84
PrintDialog::PrintPreviewWindow::~PrintPreviewWindow()
85
0
{
86
0
}
87
88
void PrintDialog::PrintPreviewWindow::Resize()
89
0
{
90
0
    Size aNewSize(GetOutputSizePixel());
91
0
    tools::Long nTextHeight = GetDrawingArea()->get_text_height();
92
    // leave small space for decoration
93
0
    aNewSize.AdjustWidth( -(nTextHeight + 2) );
94
0
    aNewSize.AdjustHeight( -(nTextHeight + 2) );
95
0
    Size aScaledSize;
96
0
    double fScale = 1.0;
97
98
    // #i106435# catch corner case of Size(0,0)
99
0
    Size aOrigSize( maOrigSize );
100
0
    if( aOrigSize.Width() < 1 )
101
0
        aOrigSize.setWidth( aNewSize.Width() );
102
0
    if( aOrigSize.Height() < 1 )
103
0
        aOrigSize.setHeight( aNewSize.Height() );
104
0
    if( aOrigSize.Width() > aOrigSize.Height() )
105
0
    {
106
0
        aScaledSize = Size( aNewSize.Width(), aNewSize.Width() * aOrigSize.Height() / aOrigSize.Width() );
107
0
        if( aScaledSize.Height() > aNewSize.Height() )
108
0
            fScale = double(aNewSize.Height())/double(aScaledSize.Height());
109
0
    }
110
0
    else
111
0
    {
112
0
        aScaledSize = Size( aNewSize.Height() * aOrigSize.Width() / aOrigSize.Height(), aNewSize.Height() );
113
0
        if( aScaledSize.Width() > aNewSize.Width() )
114
0
            fScale = double(aNewSize.Width())/double(aScaledSize.Width());
115
0
    }
116
0
    aScaledSize.setWidth( tools::Long(aScaledSize.Width()*fScale) );
117
0
    aScaledSize.setHeight( tools::Long(aScaledSize.Height()*fScale) );
118
119
0
    maPreviewSize = aScaledSize;
120
121
    // check and evtl. recreate preview bitmap
122
0
    preparePreviewBitmap();
123
0
}
124
125
void PrintDialog::PrintPreviewWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
126
0
{
127
0
    auto popIt = rRenderContext.ScopedPush();
128
0
    weld::SetPointFont(rRenderContext, rRenderContext.GetSettings().GetStyleSettings().GetLabelFont());
129
0
    rRenderContext.SetTextColor(rRenderContext.GetSettings().GetStyleSettings().GetLabelTextColor());
130
0
    rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetDialogColor()));
131
0
    rRenderContext.Erase();
132
133
0
    auto nTextHeight = rRenderContext.GetTextHeight();
134
0
    Size aSize(GetOutputSizePixel());
135
0
    Point aOffset((aSize.Width()  - maPreviewSize.Width()  + nTextHeight) / 2,
136
0
                  (aSize.Height() - maPreviewSize.Height() + nTextHeight) / 2);
137
138
    // horizontal line
139
0
    {
140
0
        auto nWidth = rRenderContext.GetTextWidth(maHorzText);
141
142
0
        auto nStart = aOffset.X() + (maPreviewSize.Width() - nWidth) / 2;
143
0
        rRenderContext.DrawText(Point(nStart, aOffset.Y() - nTextHeight), maHorzText, 0, maHorzText.getLength());
144
145
0
        DecorationView aDecoView(&rRenderContext);
146
0
        auto nTop = aOffset.Y() - (nTextHeight / 2);
147
0
        aDecoView.DrawSeparator(Point(aOffset.X(), nTop), Point(nStart - 2, nTop), false);
148
0
        aDecoView.DrawSeparator(Point(nStart + nWidth + 2, nTop), Point(aOffset.X() + maPreviewSize.Width(), nTop), false);
149
0
    }
150
151
    // vertical line
152
0
    {
153
0
        auto popIt2 = rRenderContext.ScopedPush(PushFlags::FONT);
154
0
        vcl::Font aFont(rRenderContext.GetFont());
155
0
        aFont.SetOrientation(900_deg10);
156
0
        rRenderContext.SetFont(aFont);
157
158
0
        auto nLeft = aOffset.X() - nTextHeight;
159
160
0
        auto nWidth = rRenderContext.GetTextWidth(maVertText);
161
0
        auto nStart = aOffset.Y() + (maPreviewSize.Height() + nWidth) / 2;
162
163
0
        rRenderContext.DrawText(Point(nLeft, nStart), maVertText, 0, maVertText.getLength());
164
165
0
        DecorationView aDecoView(&rRenderContext);
166
0
        nLeft = aOffset.X() - (nTextHeight / 2);
167
0
        aDecoView.DrawSeparator(Point(nLeft, aOffset.Y()), Point(nLeft, nStart - nWidth - 2), true);
168
0
        aDecoView.DrawSeparator(Point(nLeft, nStart + 2), Point(nLeft, aOffset.Y() + maPreviewSize.Height()), true);
169
0
    }
170
171
0
    if (!maReplacementString.isEmpty())
172
0
    {
173
        // replacement is active
174
0
        tools::Rectangle aTextRect(aOffset + Point(2, 2), Size(maPreviewSize.Width() - 4, maPreviewSize.Height() - 4));
175
0
        rRenderContext.DrawText(aTextRect, maReplacementString,
176
0
                                DrawTextFlags::Center | DrawTextFlags::VCenter |
177
0
                                DrawTextFlags::WordBreak | DrawTextFlags::MultiLine);
178
0
    }
179
0
    else
180
0
    {
181
0
        Bitmap aPreviewBitmap(maPreviewBitmap);
182
183
        // This explicit force-to-scale allows us to get the
184
        // mentioned best quality here. Unfortunately this is
185
        // currently not sure when using just ::DrawBitmap with
186
        // a defined size or ::DrawOutDev
187
0
        aPreviewBitmap.Scale(maPreviewSize, BmpScaleFlag::BestQuality);
188
0
        rRenderContext.DrawBitmap(aOffset, aPreviewBitmap);
189
0
    }
190
191
0
    tools::Rectangle aFrameRect(aOffset + Point(-1, -1), Size(maPreviewSize.Width() + 2, maPreviewSize.Height() + 2));
192
0
    DecorationView aDecorationView(&rRenderContext);
193
0
    aDecorationView.DrawFrame(aFrameRect, DrawFrameStyle::Group);
194
0
}
195
196
bool PrintDialog::PrintPreviewWindow::Command( const CommandEvent& rEvt )
197
0
{
198
0
    if( rEvt.GetCommand() == CommandEventId::Wheel )
199
0
    {
200
0
        const CommandWheelData* pWheelData = rEvt.GetWheelData();
201
0
        if(pWheelData->GetDelta() > 0)
202
0
            mpDialog->previewBackward();
203
0
        else if (pWheelData->GetDelta() < 0)
204
0
            mpDialog->previewForward();
205
0
        return true;
206
0
    }
207
0
    return CustomWidgetController::Command(rEvt);
208
0
}
209
210
void PrintDialog::PrintPreviewWindow::setPreview( const GDIMetaFile& i_rNewPreview,
211
                                                  const Size& i_rOrigSize,
212
                                                  std::u16string_view i_rPaperName,
213
                                                  const OUString& i_rReplacement,
214
                                                  sal_Int32 i_nDPIX,
215
                                                  sal_Int32 i_nDPIY,
216
                                                  bool i_bGreyscale
217
                                                 )
218
0
{
219
0
    maMtf = i_rNewPreview;
220
0
    mnDPIX = i_nDPIX;
221
0
    mnDPIY = i_nDPIY;
222
0
    maOrigSize = i_rOrigSize;
223
0
    maReplacementString = i_rReplacement;
224
0
    mbGreyscale = i_bGreyscale;
225
226
    // use correct measurements
227
0
    const LocaleDataWrapper& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
228
0
    o3tl::Length eUnit = o3tl::Length::mm;
229
0
    int nDigits = 0;
230
0
    if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US )
231
0
    {
232
0
        eUnit = o3tl::Length::in100;
233
0
        nDigits = 2;
234
0
    }
235
0
    Size aLogicPaperSize(o3tl::convert(i_rOrigSize, o3tl::Length::mm100, eUnit));
236
0
    OUString aNumText( rLocWrap.getNum( aLogicPaperSize.Width(), nDigits ) );
237
0
    OUStringBuffer aBuf( aNumText + " " );
238
0
    aBuf.appendAscii( eUnit == o3tl::Length::mm ? "mm" : "in" );
239
0
    if( !i_rPaperName.empty() )
240
0
    {
241
0
        aBuf.append( OUString::Concat(" (")  + i_rPaperName + ")" );
242
0
    }
243
0
    maHorzText = aBuf.makeStringAndClear();
244
245
0
    aNumText = rLocWrap.getNum( aLogicPaperSize.Height(), nDigits );
246
0
    aBuf.append( aNumText + " " );
247
0
    aBuf.appendAscii( eUnit == o3tl::Length::mm ? "mm" : "in" );
248
0
    maVertText = aBuf.makeStringAndClear();
249
250
    // We have a new Metafile and evtl. a new page, so we need to reset
251
    // the PreviewBitmap to force new creation
252
0
    maPreviewBitmap = Bitmap();
253
254
    // sets/calculates e.g. maPreviewSize
255
    // also triggers preparePreviewBitmap()
256
0
    Resize();
257
258
0
    Invalidate();
259
0
}
260
261
void PrintDialog::PrintPreviewWindow::preparePreviewBitmap()
262
0
{
263
0
    if(maPreviewSize.IsEmpty())
264
0
    {
265
        // not yet fully initialized, no need to prepare anything
266
0
        return;
267
0
    }
268
269
    // define an allowed number of pixels, also see
270
    // defaults for primitive renderers and similar. This
271
    // might be centralized and made dependent of 32/64bit
272
0
    const sal_uInt32 nMaxSquarePixels(500000);
273
274
    // check how big (squarePixels) the preview is currently (with
275
    // max value of MaxSquarePixels)
276
0
    const sal_uInt32 nCurrentSquarePixels(
277
0
        std::min(
278
0
            nMaxSquarePixels,
279
0
            static_cast<sal_uInt32>(maPreviewBitmap.GetSizePixel().getWidth())
280
0
            * static_cast<sal_uInt32>(maPreviewBitmap.GetSizePixel().getHeight())));
281
282
    // check how big (squarePixels) the preview needs to be (with
283
    // max value of MaxSquarePixels)
284
0
    const sal_uInt32 nRequiredSquarePixels(
285
0
        std::min(
286
0
            nMaxSquarePixels,
287
0
            static_cast<sal_uInt32>(maPreviewSize.getWidth())
288
0
            * static_cast<sal_uInt32>(maPreviewSize.getHeight())));
289
290
    // check if preview is big enough. Use a scaling value in
291
    // the comparison to not get bigger at the last possible moment
292
    // what may look awkward and pixelated (again). This means
293
    // to use a percentage value - if we have at least
294
    // that value of required pixels, we are good.
295
0
    static const double fPreventAwkwardFactor(1.35); // 35%
296
0
    if(nCurrentSquarePixels >= static_cast<sal_uInt32>(nRequiredSquarePixels * fPreventAwkwardFactor))
297
0
    {
298
        // at this place we also could add a mechanism to let the preview
299
        // bitmap 'shrink' again if it is currently 'too big' -> bigger
300
        // than required. I think this is not necessary for now.
301
302
        // already sufficient, done.
303
0
        return;
304
0
    }
305
306
    // check if we have enough square pixels e.g for 8x8 pixels
307
0
    if(nRequiredSquarePixels < 64)
308
0
    {
309
        // too small preview - let it empty
310
0
        return;
311
0
    }
312
313
    // Calculate nPlannedSquarePixels which is the required size
314
    // expanded by a percentage (with max value of MaxSquarePixels)
315
0
    static const double fExtraSpaceFactor(1.65); // 65%
316
0
    const sal_uInt32 nPlannedSquarePixels(
317
0
        std::min(
318
0
            nMaxSquarePixels,
319
0
            static_cast<sal_uInt32>(maPreviewSize.getWidth() * fExtraSpaceFactor)
320
0
            * static_cast<sal_uInt32>(maPreviewSize.getHeight() * fExtraSpaceFactor)));
321
322
    // calculate back new width and height - it might have been
323
    // truncated by MaxSquarePixels.
324
    // We know that w*h == nPlannedSquarePixels and w/h == ratio
325
0
    const double fRatio(static_cast<double>(maPreviewSize.getWidth()) / static_cast<double>(maPreviewSize.getHeight()));
326
0
    const double fNewWidth(sqrt(static_cast<double>(nPlannedSquarePixels) * fRatio));
327
0
    const double fNewHeight(sqrt(static_cast<double>(nPlannedSquarePixels) / fRatio));
328
0
    const Size aScaledSize(basegfx::fround<tools::Long>(fNewWidth), basegfx::fround<tools::Long>(fNewHeight));
329
330
    // check if this eventual maximum is already reached
331
    // due to having hit the MaxSquarePixels. Due to using
332
    // an integer AspectRatio, we cannot make a numeric exact
333
    // comparison - we need to compare if we are close
334
0
    const double fScaledSizeSquare(static_cast<double>(aScaledSize.getWidth() * aScaledSize.getHeight()));
335
0
    const double fPreviewSizeSquare(static_cast<double>(maPreviewBitmap.GetSizePixel().getWidth() * maPreviewBitmap.GetSizePixel().getHeight()));
336
337
    // test as equal up to 0.1% (0.001)
338
0
    if(fPreviewSizeSquare != 0.0 && fabs((fScaledSizeSquare / fPreviewSizeSquare) - 1.0) < 0.001)
339
0
    {
340
        // maximum is reached, avoid bigger scaling
341
0
        return;
342
0
    }
343
344
    // create temporary VDev with requested Size and DPI.
345
    // CAUTION: DPI *is* important here - it DIFFERS from 75x75, usually 600x600 is used
346
0
    ScopedVclPtrInstance<VirtualDevice> pPrerenderVDev(*Application::GetDefaultDevice());
347
0
    pPrerenderVDev->SetOutputSizePixel(aScaledSize, false);
348
0
    pPrerenderVDev->SetReferenceDevice( mnDPIX, mnDPIY );
349
350
    // calculate needed Scale for Metafile (using Size and DPI from VDev)
351
0
    Size aLogicSize( pPrerenderVDev->PixelToLogic( pPrerenderVDev->GetOutputSizePixel(), MapMode( MapUnit::Map100thMM ) ) );
352
0
    Size aOrigSize( maOrigSize );
353
0
    if( aOrigSize.Width() < 1 )
354
0
        aOrigSize.setWidth( aLogicSize.Width() );
355
0
    if( aOrigSize.Height() < 1 )
356
0
        aOrigSize.setHeight( aLogicSize.Height() );
357
0
    double fScale = double(aLogicSize.Width())/double(aOrigSize.Width());
358
359
    // tdf#141761
360
    // The display quality of the Preview is pretty ugly when
361
    // FormControls are used. I made a deep-dive why this happens,
362
    // and in principle the reason is the Metafile::Scale used
363
    // below. Since Metafile actions are integer, that floating point
364
    // scale leads to rounding errors that make the lines painting
365
    // the FormControls disappear in the surrounding ClipRegions.
366
    // That Scale cannot be avoided since the Metafile contains its
367
    // own SetMapMode commands which *will* be executed on ::Play,
368
    // so the ::Scale is the only possibility for Metafile currently:
369
    // Giving a Size as parameter in ::Play will *not* work due to
370
    // the relativeMapMode that gets created will fail on
371
    // ::SetMapMode actions in the Metafile - and FormControls DO
372
    // use ::SetMapMode(MapPixel).
373
    // This can only be solved better in the future using Primitives
374
    // which would allow any scale by embedding to a Transformation,
375
    // but that would be a bigger rework.
376
    // Until then, use this little 'trick' to improve quality.
377
    // It uses the fact to empirically having tested that the quality
378
    // gets really bad for FormControls starting by a scale factor
379
    // smaller than 0.2 - that makes the ClipRegion overlap start.
380
    // So - for now - try not to go below that.
381
0
    static const double fMinimumScale(0.2);
382
0
    double fFactor(0.0);
383
0
    if(fScale < fMinimumScale)
384
0
    {
385
0
        fFactor = fMinimumScale / fScale;
386
0
        fScale = fMinimumScale;
387
388
0
        double fWidth(aScaledSize.getWidth() * fFactor);
389
0
        double fHeight(aScaledSize.getHeight() * fFactor);
390
0
        const double fNewNeededPixels(fWidth * fHeight);
391
392
        // to not risk using too big bitmaps and running into
393
        // memory problems, still limit to a useful factor is
394
        // necessary, also empirically estimated to
395
        // avoid the quality from collapsing (using a direct
396
        // in-between, ceil'd result)
397
0
        static const double fMaximumQualitySquare(1396221.0);
398
399
0
        if(fNewNeededPixels > fMaximumQualitySquare)
400
0
        {
401
0
            const double fCorrection(fMaximumQualitySquare/fNewNeededPixels);
402
0
            fWidth *= fCorrection;
403
0
            fHeight *= fCorrection;
404
0
            fScale *= fCorrection;
405
0
        }
406
407
0
        const Size aScaledSize2(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight));
408
0
        pPrerenderVDev->SetOutputSizePixel(aScaledSize2, false);
409
0
        aLogicSize = pPrerenderVDev->PixelToLogic( aScaledSize2, MapMode( MapUnit::Map100thMM ) );
410
0
    }
411
412
0
    pPrerenderVDev->EnableOutput();
413
0
    pPrerenderVDev->SetBackground( Wallpaper(COL_WHITE) );
414
0
    pPrerenderVDev->Erase();
415
0
    pPrerenderVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
416
0
    if( mbGreyscale )
417
0
        pPrerenderVDev->SetDrawMode( pPrerenderVDev->GetDrawMode() |
418
0
                                ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
419
0
                                  DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
420
421
    // Copy, Scale and Paint Metafile
422
0
    GDIMetaFile aMtf( maMtf );
423
0
    aMtf.WindStart();
424
0
    aMtf.Scale( fScale, fScale );
425
0
    aMtf.WindStart();
426
0
    aMtf.Play(*pPrerenderVDev, Point(0, 0), aLogicSize);
427
428
0
    pPrerenderVDev->SetMapMode(MapMode(MapUnit::MapPixel));
429
0
    maPreviewBitmap = pPrerenderVDev->GetBitmap(Point(0, 0), pPrerenderVDev->GetOutputSizePixel());
430
431
0
    if(0.0 != fFactor)
432
0
    {
433
        // Correct to needed size, BmpScaleFlag::Interpolate is acceptable,
434
        // but BmpScaleFlag::BestQuality is just better. In case of time
435
        // constraints, change to Interpolate would be possible
436
0
        maPreviewBitmap.Scale(aScaledSize, BmpScaleFlag::BestQuality);
437
0
    }
438
0
}
439
440
PrintDialog::ShowNupOrderWindow::ShowNupOrderWindow()
441
0
    : mnOrderMode( NupOrderType::LRTB )
442
0
    , mnRows( 1 )
443
0
    , mnColumns( 1 )
444
0
{
445
0
}
446
447
void PrintDialog::ShowNupOrderWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
448
0
{
449
0
    Size aSize(70, 70);
450
0
    pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
451
0
    CustomWidgetController::SetDrawingArea(pDrawingArea);
452
0
    SetOutputSizePixel(aSize);
453
0
}
454
455
void PrintDialog::ShowNupOrderWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*i_rRect*/)
456
0
{
457
0
    rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
458
0
    rRenderContext.SetTextColor(rRenderContext.GetSettings().GetStyleSettings().GetFieldTextColor());
459
0
    rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFieldColor()));
460
0
    rRenderContext.Erase();
461
462
0
    int nPages = mnRows * mnColumns;
463
0
    Font aFont(rRenderContext.GetSettings().GetStyleSettings().GetFieldFont());
464
0
    aFont.SetFontSize(Size(0, 24));
465
0
    rRenderContext.SetFont(aFont);
466
0
    Size aSampleTextSize(rRenderContext.GetTextWidth(OUString::number(nPages + 1)), rRenderContext.GetTextHeight());
467
0
    Size aOutSize(GetOutputSizePixel());
468
0
    Size aSubSize(aOutSize.Width() / mnColumns, aOutSize.Height() / mnRows);
469
    // calculate font size: shrink the sample text so it fits
470
0
    double fX = double(aSubSize.Width()) / double(aSampleTextSize.Width());
471
0
    double fY = double(aSubSize.Height()) / double(aSampleTextSize.Height());
472
0
    double fScale = (fX < fY) ? fX : fY;
473
0
    tools::Long nFontHeight = tools::Long(24.0 * fScale) - 3;
474
0
    if (nFontHeight < 5)
475
0
        nFontHeight = 5;
476
0
    aFont.SetFontSize(Size( 0, nFontHeight));
477
0
    rRenderContext.SetFont(aFont);
478
0
    tools::Long nTextHeight = rRenderContext.GetTextHeight();
479
0
    for (int i = 0; i < nPages; i++)
480
0
    {
481
0
        OUString aPageText(OUString::number(i + 1));
482
0
        int nX = 0, nY = 0;
483
0
        switch (mnOrderMode)
484
0
        {
485
0
        case NupOrderType::LRTB:
486
0
            nX = (i % mnColumns);
487
0
            nY = (i / mnColumns);
488
0
            break;
489
0
        case NupOrderType::TBLR:
490
0
            nX = (i / mnRows);
491
0
            nY = (i % mnRows);
492
0
            break;
493
0
        case NupOrderType::RLTB:
494
0
            nX = mnColumns - 1 - (i % mnColumns);
495
0
            nY = (i / mnColumns);
496
0
            break;
497
0
        case NupOrderType::TBRL:
498
0
            nX = mnColumns - 1 - (i / mnRows);
499
0
            nY = (i % mnRows);
500
0
            break;
501
0
        }
502
0
        Size aTextSize(rRenderContext.GetTextWidth(aPageText), nTextHeight);
503
0
        int nDeltaX = (aSubSize.Width() - aTextSize.Width()) / 2;
504
0
        int nDeltaY = (aSubSize.Height() - aTextSize.Height()) / 2;
505
0
        rRenderContext.DrawText(Point(nX * aSubSize.Width() + nDeltaX,
506
0
                                      nY * aSubSize.Height() + nDeltaY), aPageText);
507
0
    }
508
0
    DecorationView aDecorationView(&rRenderContext);
509
0
    aDecorationView.DrawFrame(tools::Rectangle(Point(0, 0), aOutSize), DrawFrameStyle::Group);
510
0
}
511
512
Size const & PrintDialog::getJobPageSize()
513
0
{
514
0
    if( maFirstPageSize.IsEmpty() )
515
0
    {
516
0
        maFirstPageSize = maNupPortraitSize;
517
0
        GDIMetaFile aMtf;
518
0
        if( maPController->getPageCountProtected() > 0 )
519
0
        {
520
0
            PrinterController::PageSize aPageSize = maPController->getPageFile( 0, aMtf, true );
521
0
            maFirstPageSize = aPageSize.aSize;
522
0
        }
523
0
    }
524
0
    return maFirstPageSize;
525
0
}
526
527
PrintDialog::PrintDialog(weld::Window* i_pWindow, std::shared_ptr<PrinterController> i_xController)
528
0
    : GenericDialogController(i_pWindow, u"vcl/ui/printdialog.ui"_ustr, u"PrintDialog"_ustr)
529
0
    , maPController(std::move( i_xController ))
530
0
    , mxTabCtrl(m_xBuilder->weld_notebook(u"tabcontrol"_ustr))
531
0
    , mxScrolledWindow(m_xBuilder->weld_scrolled_window(u"scrolledwindow"_ustr))
532
0
    , mxPageLayoutFrame(m_xBuilder->weld_frame(u"layoutframe"_ustr))
533
0
    , mxPrinters(m_xBuilder->weld_combo_box(u"printersbox"_ustr))
534
0
    , mxStatusTxt(m_xBuilder->weld_label(u"status"_ustr))
535
0
    , mxSetupButton(m_xBuilder->weld_button(u"setup"_ustr))
536
0
    , mxCopyCountField(m_xBuilder->weld_spin_button(u"copycount"_ustr))
537
0
    , mxCollateBox(m_xBuilder->weld_check_button(u"collate"_ustr))
538
0
    , mxCollateImage(m_xBuilder->weld_image(u"collateimage"_ustr))
539
0
    , mxPageRangeEdit(m_xBuilder->weld_entry(u"pagerange"_ustr))
540
0
    , mxPageRangesRadioButton(m_xBuilder->weld_radio_button(u"rbRangePages"_ustr))
541
0
    , mxPaperSidesBox(m_xBuilder->weld_combo_box(u"sidesbox"_ustr))
542
0
    , mxSingleJobsBox(m_xBuilder->weld_check_button(u"singlejobs"_ustr))
543
0
    , mxReverseOrderBox(m_xBuilder->weld_check_button(u"reverseorder"_ustr))
544
0
    , mxOKButton(m_xBuilder->weld_button(u"ok"_ustr))
545
0
    , mxCancelButton(m_xBuilder->weld_button(u"cancel"_ustr))
546
0
    , mxBackwardBtn(m_xBuilder->weld_button(u"backward"_ustr))
547
0
    , mxForwardBtn(m_xBuilder->weld_button(u"forward"_ustr))
548
0
    , mxFirstBtn(m_xBuilder->weld_button(u"btnFirst"_ustr))
549
0
    , mxLastBtn(m_xBuilder->weld_button(u"btnLast"_ustr))
550
0
    , mxPreviewBox(m_xBuilder->weld_check_button(u"previewbox"_ustr))
551
0
    , mxNumPagesText(m_xBuilder->weld_label(u"totalnumpages"_ustr))
552
0
    , mxPreview(new PrintPreviewWindow(this))
553
0
    , mxPreviewWindow(new weld::CustomWeld(*m_xBuilder, u"preview"_ustr, *mxPreview))
554
0
    , mxPageEdit(m_xBuilder->weld_entry(u"pageedit"_ustr))
555
0
    , mxPagesBtn(m_xBuilder->weld_radio_button(u"pagespersheetbtn"_ustr))
556
0
    , mxBrochureBtn(m_xBuilder->weld_radio_button(u"brochure"_ustr))
557
0
    , mxPagesBoxTitleTxt(m_xBuilder->weld_label(u"pagespersheettxt"_ustr))
558
0
    , mxNupPagesBox(m_xBuilder->weld_combo_box(u"pagespersheetbox"_ustr))
559
0
    , mxNupNumPagesTxt(m_xBuilder->weld_label(u"pagestxt"_ustr))
560
0
    , mxNupColEdt(m_xBuilder->weld_spin_button(u"pagecols"_ustr))
561
0
    , mxNupTimesTxt(m_xBuilder->weld_label(u"by"_ustr))
562
0
    , mxNupRowsEdt(m_xBuilder->weld_spin_button(u"pagerows"_ustr))
563
0
    , mxPageMarginTxt1(m_xBuilder->weld_label(u"pagemargintxt1"_ustr))
564
0
    , mxPageMarginEdt(m_xBuilder->weld_metric_spin_button(u"pagemarginsb"_ustr, FieldUnit::MM))
565
0
    , mxPageMarginTxt2(m_xBuilder->weld_label(u"pagemargintxt2"_ustr))
566
0
    , mxSheetMarginTxt1(m_xBuilder->weld_label(u"sheetmargintxt1"_ustr))
567
0
    , mxSheetMarginEdt(m_xBuilder->weld_metric_spin_button(u"sheetmarginsb"_ustr, FieldUnit::MM))
568
0
    , mxSheetMarginTxt2(m_xBuilder->weld_label(u"sheetmargintxt2"_ustr))
569
0
    , mxPaperSizeBox(m_xBuilder->weld_combo_box(u"papersizebox"_ustr))
570
0
    , mxOrientationBox(m_xBuilder->weld_combo_box(u"pageorientationbox"_ustr))
571
0
    , mxNupOrderTxt(m_xBuilder->weld_label(u"labelorder"_ustr))
572
0
    , mxNupOrderBox(m_xBuilder->weld_combo_box(u"orderbox"_ustr))
573
0
    , mxNupOrder(new ShowNupOrderWindow)
574
0
    , mxNupOrderWin(new weld::CustomWeld(*m_xBuilder, u"orderpreview"_ustr, *mxNupOrder))
575
0
    , mxBorderCB(m_xBuilder->weld_check_button(u"bordercb"_ustr))
576
0
    , mxRangeExpander(m_xBuilder->weld_expander(u"exRangeExpander"_ustr))
577
0
    , mxLayoutExpander(m_xBuilder->weld_expander(u"exLayoutExpander"_ustr))
578
0
    , mxCustom(m_xBuilder->weld_widget(u"customcontents"_ustr))
579
0
    , maPrintToFileText( VclResId( SV_PRINT_TOFILE_TXT ) )
580
0
    , maDefPrtText( VclResId( SV_PRINT_DEFPRT_TXT ) )
581
0
    , maNoPageStr( VclResId( SV_PRINT_NOPAGES ) )
582
0
    , maNoPreviewStr( VclResId( SV_PRINT_NOPREVIEW ) )
583
0
    , mnInitialPage( 0 )
584
0
    , mnCurPage( 0 )
585
0
    , mnCachedPages( 0 )
586
0
    , mbShowLayoutFrame( true )
587
0
    , mbHasCustomPaperEntry( false )
588
0
    , maUpdatePreviewIdle("Print Dialog Update Preview Idle")
589
0
    , maPageRangeUpdateTimer("PageRangeUpdateTimer")
590
0
{
591
    // Retrieve current page, it'll be used later in updatePageRange method
592
0
    PropertyValue* pPageRange = maPController->getValue(u"PageRange"_ustr);
593
0
    if (pPageRange && pPageRange->Value.hasValue())
594
0
    {
595
0
        OUString aPageRange;
596
0
        pPageRange->Value >>= aPageRange;
597
0
        mnInitialPage = aPageRange.toInt32();
598
0
    }
599
600
    // save printbutton text, gets exchanged occasionally with print to file
601
0
    maPrintText = mxOKButton->get_label();
602
603
0
    maPageStr = mxNumPagesText->get_label();
604
605
0
    mxPrinters->append_text(maPrintToFileText);
606
    // fill printer listbox
607
0
    std::vector< OUString > rQueues( Printer::GetPrinterQueues() );
608
0
    std::sort( rQueues.begin(), rQueues.end(), lcl_ListBoxCompare );
609
0
    for( const auto& rQueue : rQueues )
610
0
    {
611
0
        mxPrinters->append_text(rQueue);
612
0
    }
613
    // select current printer
614
0
    if (mxPrinters->find_text(maPController->getPrinter()->GetName()) != -1)
615
0
        mxPrinters->set_active_text(maPController->getPrinter()->GetName());
616
0
    else
617
0
    {
618
        // fall back to last printer
619
0
        OUString aValue( officecfg::VCL::VCLSettings::PrintDialog::LastPrinter::get() );
620
0
        if (mxPrinters->find_text(aValue) != -1)
621
0
        {
622
0
            mxPrinters->set_active_text(aValue);
623
0
            maPController->setPrinter( VclPtrInstance<Printer>( aValue ) );
624
0
        }
625
0
        else
626
0
        {
627
            // fall back to default printer
628
0
            mxPrinters->set_active_text(Printer::GetDefaultPrinterName());
629
0
            maPController->setPrinter( VclPtrInstance<Printer>( Printer::GetDefaultPrinterName() ) );
630
0
        }
631
0
    }
632
633
    // not printing to file
634
0
    maPController->resetPrinterOptions( false );
635
636
    // update the text fields for the printer
637
0
    updatePrinterText();
638
639
    // setup dependencies
640
0
    checkControlDependencies();
641
642
    // setup paper sides box
643
0
    setupPaperSidesBox();
644
645
    // set initial focus to "Printer"
646
0
    mxPrinters->grab_focus();
647
648
    // setup sizes for N-Up
649
0
    Size aNupSize( maPController->getPrinter()->PixelToLogic(
650
0
                         maPController->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ) );
651
0
    if( maPController->getPrinter()->GetOrientation() == Orientation::Landscape )
652
0
    {
653
0
        maNupLandscapeSize = aNupSize;
654
        // coverity[swapped_arguments : FALSE] - this is in the correct order
655
0
        maNupPortraitSize = Size( aNupSize.Height(), aNupSize.Width() );
656
0
    }
657
0
    else
658
0
    {
659
0
        maNupPortraitSize = aNupSize;
660
        // coverity[swapped_arguments : FALSE] - this is in the correct order
661
0
        maNupLandscapeSize = Size( aNupSize.Height(), aNupSize.Width() );
662
0
    }
663
664
0
    maUpdatePreviewIdle.SetPriority(TaskPriority::POST_PAINT);
665
0
    maUpdatePreviewIdle.SetInvokeHandler(LINK( this, PrintDialog, updatePreviewIdle));
666
667
0
    maPageRangeUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
668
0
    maPageRangeUpdateTimer.SetInvokeHandler(LINK(this, PrintDialog, PageRangeUpdateTimerHdl));
669
670
671
0
    initFromMultiPageSetup( maPController->getMultipage() );
672
673
    // setup optional UI options set by application
674
0
    setupOptionalUI();
675
676
    // hide layout frame if unwanted
677
0
    mxPageLayoutFrame->set_visible(mbShowLayoutFrame);
678
679
    // restore settings from last run
680
0
    readFromSettings();
681
682
    // setup click hdl
683
0
    mxOKButton->connect_clicked(LINK(this, PrintDialog, ClickOKCancelHdl));
684
0
    mxCancelButton->connect_clicked(LINK(this, PrintDialog, ClickOKCancelHdl));
685
0
    mxSetupButton->connect_clicked(LINK(this, PrintDialog, ClickSetupHdl));
686
0
    mxBackwardBtn->connect_clicked(LINK(this, PrintDialog, ClickBackwardHdl));
687
0
    mxForwardBtn->connect_clicked(LINK(this, PrintDialog, ClickForwardHdl));
688
0
    mxFirstBtn->connect_clicked(LINK(this, PrintDialog, ClickFirstHdl));
689
0
    mxLastBtn->connect_clicked(LINK(this, PrintDialog, ClickLastHdl));
690
691
    // setup toggle hdl
692
0
    mxReverseOrderBox->connect_toggled(LINK(this, PrintDialog, ToggleReverseOrderHdl));
693
0
    mxCollateBox->connect_toggled(LINK(this, PrintDialog, ToggleCollateHdl));
694
0
    mxSingleJobsBox->connect_toggled(LINK(this, PrintDialog, ToggleSingleJobsHdl));
695
0
    mxBrochureBtn->connect_toggled(LINK(this, PrintDialog, ToggleBrochureHdl));
696
0
    mxPreviewBox->connect_toggled(LINK(this, PrintDialog, TogglePreviewHdl));
697
0
    mxBorderCB->connect_toggled(LINK(this, PrintDialog, ToggleBorderHdl));
698
699
    // setup select hdl
700
0
    mxPrinters->connect_changed(LINK(this, PrintDialog, SelectPrinterHdl));
701
0
    mxPaperSidesBox->connect_changed(LINK(this, PrintDialog, SelectPaperSidesHdl));
702
0
    mxNupPagesBox->connect_changed(LINK(this, PrintDialog, SelectNupPagesHdl));
703
0
    mxOrientationBox->connect_changed(LINK(this, PrintDialog, SelectOrientationHdl));
704
0
    mxNupOrderBox->connect_changed(LINK(this, PrintDialog, SelectNupOrderHdl));
705
0
    mxPaperSizeBox->connect_changed(LINK(this, PrintDialog, SelectPaperSizeHdl));
706
707
    // setup modify hdl
708
0
    mxPageEdit->connect_activate( LINK( this, PrintDialog, ActivateHdl ) );
709
0
    mxPageEdit->connect_focus_out( LINK( this, PrintDialog, FocusOutHdl ) );
710
0
    mxCopyCountField->connect_value_changed( LINK( this, PrintDialog, SpinModifyHdl ) );
711
0
    mxNupColEdt->connect_value_changed( LINK( this, PrintDialog, SpinModifyHdl ) );
712
0
    mxNupRowsEdt->connect_value_changed( LINK( this, PrintDialog, SpinModifyHdl ) );
713
0
    mxPageMarginEdt->connect_value_changed( LINK( this, PrintDialog, MetricSpinModifyHdl ) );
714
0
    mxSheetMarginEdt->connect_value_changed( LINK( this, PrintDialog, MetricSpinModifyHdl ) );
715
716
0
    updateNupFromPages();
717
718
    // tdf#129180 Delay setting the default value in the Paper Size list
719
    // set paper sizes listbox
720
0
    setPaperSizes();
721
    // Sync selected paper size with the controller for preview.
722
0
    if (mxPaperSizeBox->get_sensitive() && mxPaperSizeBox->get_active() != -1)
723
0
        SelectPaperSizeHdl(*mxPaperSizeBox);
724
725
0
    mxRangeExpander->set_expanded(
726
0
        officecfg::Office::Common::Print::Dialog::RangeSectionExpanded::get());
727
0
    mxLayoutExpander->set_expanded(
728
0
        officecfg::Office::Common::Print::Dialog::LayoutSectionExpanded::get());
729
730
    // lock the dialog height, regardless of later expander state
731
0
    mxScrolledWindow->set_size_request(
732
0
        mxScrolledWindow->get_preferred_size().Width() + mxScrolledWindow->get_scroll_thickness(),
733
0
        450);
734
735
0
    m_xDialog->set_centered_on_parent(true);
736
0
}
737
738
void PrintDialog::ImplDestroy()
739
0
{
740
0
    std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
741
0
    officecfg::Office::Common::Print::Dialog::RangeSectionExpanded::set(mxRangeExpander->get_expanded(), batch);
742
0
    officecfg::Office::Common::Print::Dialog::LayoutSectionExpanded::set(mxLayoutExpander->get_expanded(), batch);
743
0
    batch->commit();
744
0
}
745
746
PrintDialog::~PrintDialog()
747
0
{
748
0
    suppress_fun_call_w_exception(ImplDestroy());
749
0
}
750
751
void PrintDialog::setupPaperSidesBox()
752
0
{
753
0
    DuplexMode eDuplex = maPController->getPrinter()->GetDuplexMode();
754
755
0
    if ( eDuplex == DuplexMode::Unknown || isPrintToFile() )
756
0
    {
757
0
        mxPaperSidesBox->set_active( 0 );
758
0
        mxPaperSidesBox->set_sensitive( false );
759
0
    }
760
0
    else
761
0
    {
762
0
        mxPaperSidesBox->set_active( static_cast<sal_Int32>(eDuplex) - 1 );
763
0
        mxPaperSidesBox->set_sensitive( true );
764
0
    }
765
0
}
766
767
void PrintDialog::storeToSettings()
768
0
{
769
0
    std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
770
771
0
    officecfg::VCL::VCLSettings::PrintDialog::LastPrinter::set(
772
0
                      isPrintToFile() ? Printer::GetDefaultPrinterName()
773
0
                                      : mxPrinters->get_active_text(), batch );
774
775
0
    officecfg::VCL::VCLSettings::PrintDialog::LastPage::set(
776
0
                     mxTabCtrl->get_tab_label_text(mxTabCtrl->get_current_page_ident()), batch);
777
778
0
    officecfg::VCL::VCLSettings::PrintDialog::WindowState::set(
779
0
        m_xDialog->get_window_state(vcl::WindowDataMask::All).toStr(), batch);
780
781
0
    officecfg::VCL::VCLSettings::PrintDialog::Collate::set( mxCollateBox->get_active(), batch );
782
783
0
    officecfg::VCL::VCLSettings::PrintDialog::CollateSingleJobs::set(
784
0
                     mxSingleJobsBox->get_active(), batch );
785
786
0
    officecfg::VCL::VCLSettings::PrintDialog::HasPreview::set( hasPreview(), batch);
787
788
0
    batch->commit();
789
0
}
790
791
void PrintDialog::readFromSettings()
792
0
{
793
    // read last selected tab page; if it exists, activate it
794
0
    OUString aValue = officecfg::VCL::VCLSettings::PrintDialog::LastPage::get();
795
0
    sal_uInt16 nCount = mxTabCtrl->get_n_pages();
796
0
    for (sal_uInt16 i = 0; i < nCount; ++i)
797
0
    {
798
0
        OUString sPageId = mxTabCtrl->get_page_ident(i);
799
0
        if (aValue == mxTabCtrl->get_tab_label_text(sPageId))
800
0
        {
801
0
            mxTabCtrl->set_current_page(sPageId);
802
0
            break;
803
0
        }
804
0
    }
805
806
    // persistent window state
807
0
    aValue = officecfg::VCL::VCLSettings::PrintDialog::WindowState::get();
808
0
    if (!aValue.isEmpty())
809
0
        m_xDialog->set_window_state(vcl::WindowData(aValue));
810
811
    // collate
812
0
    mxCollateBox->set_sensitive( officecfg::VCL::VCLSettings::PrintDialog::Collate::isReadOnly() );
813
0
    mxCollateBox->set_active( officecfg::VCL::VCLSettings::PrintDialog::Collate::get() );
814
815
    // collate single jobs
816
0
    mxSingleJobsBox->set_active( officecfg::VCL::VCLSettings::PrintDialog::CollateSingleJobs::get() );
817
818
    // preview box
819
0
    mxPreviewBox->set_active( officecfg::VCL::VCLSettings::PrintDialog::HasPreview::get() );
820
821
0
}
822
823
void PrintDialog::setPaperSizes()
824
0
{
825
0
    mxPaperSizeBox->clear();
826
0
    mxPaperSizeBox->set_active(-1);
827
0
    mbHasCustomPaperEntry = false;
828
829
0
    VclPtr<Printer> aPrt( maPController->getPrinter() );
830
0
    mePaper = aPrt->GetPaper();
831
832
    // Related: tdf#159995 allow setting the page size when printing to file
833
    // On macOS, printing to file just saves to PDF using a native print
834
    // job just like the native print dialog's PDF > Save as PDF... option.
835
    // Also, as noted in tdf#163558, the native print dialog could support
836
    // changing the page size eventually (and already does in Apple's
837
    // Safari and TextEdit applications) so allow changing of the page
838
    // size in the non-native print dialog on macOS as well.
839
0
#ifndef MACOSX
840
0
    if ( isPrintToFile() )
841
0
    {
842
0
        mxPaperSizeBox->set_sensitive( false );
843
0
    }
844
0
    else
845
0
#endif
846
0
    {
847
0
        int nExactMatch = -1;
848
0
        int nExactRotatedMatch = -1;
849
0
        int nSizeMatch = -1;
850
0
        int nRotatedSizeMatch = -1;
851
0
        Size aSizeOfPaper = aPrt->GetSizeOfPaper();
852
0
        PaperInfo aPaperInfo(aSizeOfPaper.getWidth(), aSizeOfPaper.getHeight());
853
        // coverity[swapped_arguments : FALSE] - this is in the correct order
854
0
        PaperInfo aRotatedPaperInfo(aSizeOfPaper.getHeight(), aSizeOfPaper.getWidth());
855
0
        const LocaleDataWrapper& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
856
0
        o3tl::Length eUnit = o3tl::Length::mm;
857
0
        int nDigits = 0;
858
0
        if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US )
859
0
        {
860
0
            eUnit = o3tl::Length::in100;
861
0
            nDigits = 2;
862
0
        }
863
864
        // Also match by document page dimensions, not just Paper enum
865
        // (e.g. a card printer whose paper has no matching enum).
866
0
        Size aDocSize = getJobPageSize();
867
0
        PaperInfo aDocPaperInfo(aDocSize.getWidth(), aDocSize.getHeight());
868
0
        PaperInfo aDocRotatedInfo(aDocSize.getHeight(), aDocSize.getWidth());
869
0
        int nDocDimMatch = -1;
870
0
        int nDocDimRotatedMatch = -1;
871
872
0
        for (int nPaper = 0; nPaper < aPrt->GetPaperInfoCount(); nPaper++)
873
0
        {
874
0
            PaperInfo aInfo = aPrt->GetPaperInfo( nPaper );
875
0
            aInfo.doSloppyFit(true);
876
0
            Paper ePaper = aInfo.getPaper();
877
878
            // Use PaperInfo directly (1/100th mm); GetPaperSize() uses
879
            // PixelToLogic which can return wrong values.
880
0
            Size aSize(aInfo.getWidth(), aInfo.getHeight());
881
0
            Size aLogicPaperSize( o3tl::convert(aSize, o3tl::Length::mm100, eUnit) );
882
883
0
            OUString aWidth( rLocWrap.getNum( aLogicPaperSize.Width(), nDigits ) );
884
0
            OUString aHeight( rLocWrap.getNum( aLogicPaperSize.Height(), nDigits ) );
885
0
            OUString aUnit = eUnit == o3tl::Length::mm ? u"mm"_ustr : u"in"_ustr;
886
0
            OUString aPaperName;
887
888
            // Paper sizes that we don't know of but the system printer driver lists are not "User
889
            // Defined". Displaying them as such is just confusing.
890
0
            if (ePaper != PAPER_USER)
891
0
                aPaperName = Printer::GetPaperName( ePaper ) + " ";
892
893
0
            aPaperName += aWidth + aUnit + " x " + aHeight + aUnit;
894
895
0
            mxPaperSizeBox->append_text(aPaperName);
896
897
            // For exact match, check that paper orientation also matches
898
0
            if (ePaper != PAPER_USER && ePaper == mePaper)
899
0
            {
900
                // Only get the first match, in case there are multiple papers with the same size
901
0
                if (nExactMatch == -1 && aInfo.sloppyEqual(aPaperInfo))
902
0
                    nExactMatch = nPaper;
903
0
                else if (nExactRotatedMatch == -1 && aInfo.sloppyEqual(aRotatedPaperInfo))
904
0
                    nExactRotatedMatch = nPaper;
905
0
            }
906
907
0
            if (ePaper == PAPER_USER && aInfo.sloppyEqual(aPaperInfo))
908
0
                nSizeMatch = nPaper;
909
910
0
            if (ePaper == PAPER_USER && aInfo.sloppyEqual(aRotatedPaperInfo))
911
0
                nRotatedSizeMatch = nPaper;
912
913
            // Match by document page dimensions
914
0
            if (!aDocSize.IsEmpty())
915
0
            {
916
0
                if (nDocDimMatch == -1 && aInfo.sloppyEqual(aDocPaperInfo))
917
0
                    nDocDimMatch = nPaper;
918
0
                if (nDocDimRotatedMatch == -1 && aInfo.sloppyEqual(aDocRotatedInfo))
919
0
                    nDocDimRotatedMatch = nPaper;
920
0
            }
921
0
        }
922
923
        // Select the best match: exact enum > exact rotated > size > rotated size
924
0
        if (nExactMatch != -1)
925
0
            mxPaperSizeBox->set_active(nExactMatch);
926
0
        else if (nExactRotatedMatch != -1)
927
0
            mxPaperSizeBox->set_active(nExactRotatedMatch);
928
0
        else if (nSizeMatch != -1)
929
0
            mxPaperSizeBox->set_active(nSizeMatch);
930
0
        else if (nRotatedSizeMatch != -1)
931
0
            mxPaperSizeBox->set_active(nRotatedSizeMatch);
932
933
        // Override with document dimension match if enum matching failed.
934
0
        if (nDocDimMatch != -1)
935
0
        {
936
0
            mxPaperSizeBox->set_active(nDocDimMatch);
937
0
        }
938
0
        else if (nDocDimRotatedMatch != -1)
939
0
        {
940
0
            mxPaperSizeBox->set_active(nDocDimRotatedMatch);
941
0
        }
942
943
        // No printer paper matches — probe the driver for custom size.
944
        // If rejected (e.g. size below driver minimum), fall back to
945
        // the driver's chosen paper.
946
0
        if (!aDocSize.IsEmpty() && nDocDimMatch == -1 && nDocDimRotatedMatch == -1)
947
0
        {
948
            // Save orientation: SetPaperSizeUser forces Portrait.
949
0
            Orientation eOrientBeforeProbe = aPrt->GetOrientation();
950
0
            {
951
0
                auto popIt = aPrt->ScopedPush();
952
0
                aPrt->SetMapMode(MapMode(MapUnit::Map100thMM));
953
0
                aPrt->SetPaperSizeUser(aDocSize);
954
0
            }
955
            // MapMode restored by ScopedPush; paper size persists.
956
0
            Size aDriverSize = aPrt->GetSizeOfPaper();
957
0
            PaperInfo aDriverInfo(aDriverSize.getWidth(), aDriverSize.getHeight());
958
0
            aDriverInfo.doSloppyFit(true);
959
0
            bool bDriverAccepted = aDriverInfo.sloppyEqual(aDocPaperInfo)
960
0
                                || aDriverInfo.sloppyEqual(aDocRotatedInfo);
961
962
0
            if (bDriverAccepted)
963
0
            {
964
0
                Size aLogicDocSize(o3tl::convert(aDocSize, o3tl::Length::mm100, eUnit));
965
0
                OUString aWidth(rLocWrap.getNum(aLogicDocSize.Width(), nDigits));
966
0
                OUString aHeight(rLocWrap.getNum(aLogicDocSize.Height(), nDigits));
967
0
                OUString aUnitStr = eUnit == o3tl::Length::mm ? u"mm"_ustr : u"in"_ustr;
968
969
0
                OUString aPaperName = Printer::GetPaperName(PAPER_USER) + " "
970
0
                                    + aWidth + aUnitStr + " x " + aHeight + aUnitStr;
971
972
0
                mxPaperSizeBox->append_text(aPaperName);
973
0
                mxPaperSizeBox->set_active(aPrt->GetPaperInfoCount());
974
0
                maCustomPaperSize = aDocSize;
975
0
                mbHasCustomPaperEntry = true;
976
0
            }
977
0
            else
978
0
            {
979
                // Driver rejected/clamped — fall back to driver's paper
980
                // and restore orientation clobbered by SetPaperSizeUser.
981
0
                aPrt->SetOrientation(eOrientBeforeProbe);
982
0
                mePaper = aPrt->GetPaper();
983
0
                PaperInfo aFallbackInfo(aDriverSize.getWidth(), aDriverSize.getHeight());
984
0
                PaperInfo aFallbackRotated(aDriverSize.getHeight(), aDriverSize.getWidth());
985
0
                for (int nPaper = 0; nPaper < aPrt->GetPaperInfoCount(); nPaper++)
986
0
                {
987
0
                    PaperInfo aInfo = aPrt->GetPaperInfo(nPaper);
988
0
                    aInfo.doSloppyFit(true);
989
0
                    if (aInfo.sloppyEqual(aFallbackInfo) || aInfo.sloppyEqual(aFallbackRotated))
990
0
                    {
991
0
                        mxPaperSizeBox->set_active(nPaper);
992
0
                        break;
993
0
                    }
994
0
                }
995
0
            }
996
0
        }
997
998
0
        mxPaperSizeBox->set_sensitive( true );
999
0
    }
1000
0
}
1001
1002
void PrintDialog::updatePrinterText()
1003
0
{
1004
0
    const OUString aDefPrt( Printer::GetDefaultPrinterName() );
1005
0
    const QueueInfo* pInfo = Printer::GetQueueInfo( mxPrinters->get_active_text(), true );
1006
0
    if( pInfo )
1007
0
    {
1008
        // FIXME: status text
1009
0
        OUString aStatus;
1010
0
        if( aDefPrt == pInfo->GetPrinterName() )
1011
0
            aStatus = maDefPrtText;
1012
0
        mxStatusTxt->set_label( aStatus );
1013
0
    }
1014
0
    else
1015
0
    {
1016
0
        mxStatusTxt->set_label( OUString() );
1017
0
    }
1018
0
}
1019
1020
void PrintDialog::setPreviewText()
1021
0
{
1022
0
    OUString aNewText( maPageStr.replaceFirst( "%n", OUString::number( mnCachedPages ) ) );
1023
0
    mxNumPagesText->set_label( aNewText );
1024
0
}
1025
1026
void PrintDialog::schedulePreviewUpdate(bool i_bMayUseCache)
1027
0
{
1028
0
    if (!i_bMayUseCache)
1029
0
        mbUseCacheForPreview = false; // Disable cache once
1030
0
    maUpdatePreviewIdle.Start();
1031
0
}
1032
1033
IMPL_LINK_NOARG(PrintDialog, updatePreviewIdle, Timer*, void)
1034
0
{
1035
    // cache is allowed, unless explicitly disabled for this update
1036
0
    preparePreview(std::exchange(mbUseCacheForPreview, true));
1037
0
}
1038
1039
IMPL_LINK_NOARG(PrintDialog, PageRangeUpdateTimerHdl, Timer*, void)
1040
0
{
1041
0
    schedulePreviewUpdate(false);
1042
0
}
1043
1044
1045
void PrintDialog::preparePreview( bool i_bMayUseCache )
1046
0
{
1047
0
    VclPtr<Printer> aPrt( maPController->getPrinter() );
1048
    // Prefer maSelectedPaperSize (stable 1/100th mm) over PixelToLogic
1049
    // which can return wrong values during MapMode transitions.
1050
0
    Size aCurPageSize = !maSelectedPaperSize.IsEmpty()
1051
0
        ? maSelectedPaperSize
1052
0
        : aPrt->PixelToLogic( aPrt->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) );
1053
    // maSelectedPaperSize is always portrait-order; swap to landscape
1054
    // based on the orientation dropdown (not GetOrientation() which
1055
    // can be clobbered by SetPaper/SetPaperSizeUser).
1056
0
    if (!maSelectedPaperSize.IsEmpty()
1057
0
        && aCurPageSize.Width() < aCurPageSize.Height())
1058
0
    {
1059
0
        int nOrient = mxOrientationBox->get_active();
1060
0
        bool bSwapToLandscape = false;
1061
0
        if (nOrient == ORIENTATION_AUTOMATIC)
1062
0
        {
1063
0
            Size aDocPageSize = getJobPageSize();
1064
0
            bSwapToLandscape = !aDocPageSize.IsEmpty()
1065
0
                             && aDocPageSize.getWidth() > aDocPageSize.getHeight();
1066
0
        }
1067
0
        else if (nOrient == ORIENTATION_LANDSCAPE)
1068
0
        {
1069
0
            bSwapToLandscape = true;
1070
0
        }
1071
0
        if (bSwapToLandscape)
1072
0
        {
1073
            // coverity[swapped_arguments : FALSE] - this is in the correct order
1074
0
            aCurPageSize = Size(aCurPageSize.Height(), aCurPageSize.Width());
1075
0
        }
1076
0
    }
1077
    // tdf#123076 Get paper size for the preview top label
1078
0
    mePaper = aPrt->GetPaper();
1079
0
    GDIMetaFile aMtf;
1080
1081
    // Use custom paper size for preview even if the driver rejected it
1082
    // (e.g. "Print to PDF" reverts DMPAPER_USER to A4).
1083
0
    const bool bUseCustomPreviewSize = mbHasCustomPaperEntry
1084
0
        && mxPaperSizeBox->get_active() == aPrt->GetPaperInfoCount();
1085
0
    if (bUseCustomPreviewSize)
1086
0
    {
1087
0
        aCurPageSize = maCustomPaperSize;
1088
0
        mePaper = PAPER_USER;
1089
0
    }
1090
1091
    // page range may have changed depending on options
1092
0
    sal_Int32 nPages = maPController->getFilteredPageCount();
1093
0
    mnCachedPages = nPages;
1094
1095
0
    if (!i_bMayUseCache)
1096
0
        updatePageRange(nPages);
1097
1098
0
    setPreviewText();
1099
1100
0
    if ( !hasPreview() )
1101
0
    {
1102
0
        mxPreview->setPreview( aMtf, aCurPageSize,
1103
0
                            Printer::GetPaperName( mePaper ),
1104
0
                            maNoPreviewStr,
1105
0
                            aPrt->GetDPIX(), aPrt->GetDPIY(),
1106
0
                            aPrt->GetPrinterOptions().IsConvertToGreyscales()
1107
0
                            );
1108
1109
0
        mxForwardBtn->set_sensitive( false );
1110
0
        mxBackwardBtn->set_sensitive( false );
1111
0
        mxFirstBtn->set_sensitive( false );
1112
0
        mxLastBtn->set_sensitive( false );
1113
1114
0
        mxPageEdit->set_sensitive( false );
1115
1116
0
        return;
1117
0
    }
1118
1119
0
    if( mnCurPage >= nPages )
1120
0
        mnCurPage = nPages-1;
1121
0
    if( mnCurPage < 0 )
1122
0
        mnCurPage = 0;
1123
0
    mxPageEdit->set_text(OUString::number(mnCurPage + 1));
1124
1125
0
    if( nPages > 0 )
1126
0
    {
1127
0
        PrinterController::PageSize aPageSize =
1128
0
            maPController->getFilteredPageFile( mnCurPage, aMtf, i_bMayUseCache );
1129
1130
0
        if (!bUseCustomPreviewSize
1131
0
            && mxOrientationBox->get_active() == ORIENTATION_AUTOMATIC)
1132
0
        {
1133
            // tdf#173544: with N-up, getFilteredPageFile() has just composed
1134
            // the sheet and returns its size. Same condition getRealPaperSize()
1135
            // uses to pick the sheet.
1136
0
            const PrinterController::MultiPageSetup& rMPS = maPController->getMultipage();
1137
0
            if (rMPS.nRows * rMPS.nColumns > 1 && !aPageSize.aSize.IsEmpty())
1138
0
            {
1139
0
                aCurPageSize = aPageSize.aSize;
1140
0
            }
1141
0
            else
1142
0
            {
1143
0
                aCurPageSize = !maSelectedPaperSize.IsEmpty()
1144
0
                    ? maSelectedPaperSize
1145
0
                    : aPrt->PixelToLogic(aPrt->GetPaperSizePixel(), MapMode(MapUnit::Map100thMM));
1146
                // Swap to landscape if document requires it.
1147
0
                if (!maSelectedPaperSize.IsEmpty())
1148
0
                {
1149
0
                    Size aDocPageSize = getJobPageSize();
1150
0
                    bool bDocLandscape = !aDocPageSize.IsEmpty()
1151
0
                                       && aDocPageSize.getWidth() > aDocPageSize.getHeight();
1152
0
                    if (bDocLandscape && aCurPageSize.Width() < aCurPageSize.Height())
1153
0
                        aCurPageSize = Size(aCurPageSize.Height(), aCurPageSize.Width());
1154
0
                }
1155
0
            }
1156
0
        }
1157
1158
0
        if( ! aPageSize.bFullPaper )
1159
0
        {
1160
0
            const MapMode aMapMode( MapUnit::Map100thMM );
1161
0
            Point aOff( aPrt->PixelToLogic( aPrt->GetPageOffsetPixel(), aMapMode ) );
1162
0
            aMtf.Move( aOff.X(), aOff.Y() );
1163
0
        }
1164
        // tdf#150561: page size may have changed so sync mePaper with it
1165
0
        if (!bUseCustomPreviewSize)
1166
0
            mePaper = aPrt->GetPaper();
1167
0
    }
1168
1169
0
    mxPreview->setPreview( aMtf, aCurPageSize,
1170
0
                                Printer::GetPaperName( mePaper ),
1171
0
                                nPages > 0 ? OUString() : maNoPageStr,
1172
0
                                aPrt->GetDPIX(), aPrt->GetDPIY(),
1173
0
                                aPrt->GetPrinterOptions().IsConvertToGreyscales()
1174
0
                               );
1175
1176
0
    mxForwardBtn->set_sensitive( mnCurPage < nPages-1 );
1177
0
    mxBackwardBtn->set_sensitive( mnCurPage != 0 );
1178
0
    mxFirstBtn->set_sensitive( mnCurPage != 0 );
1179
0
    mxLastBtn->set_sensitive( mnCurPage < nPages-1 );
1180
0
    mxPageEdit->set_sensitive( nPages > 1 );
1181
0
}
1182
1183
void PrintDialog::updatePageRange(sal_Int32 nPages)
1184
0
{
1185
0
    if (nPages <= 0)
1186
0
        return;
1187
1188
0
    PropertyValue* pContentVal = maPController->getValue(u"PrintContent"_ustr);
1189
0
    sal_Int32 nContent = -1;
1190
0
    if (pContentVal)
1191
0
        pContentVal->Value >>= nContent;
1192
1193
0
    if (nContent == 1)
1194
0
        return; // User-controlled page range
1195
1196
0
    OUString sRange;
1197
1198
0
    if (nContent == 3)
1199
0
    {
1200
        // Current page: preserve the document page selected when
1201
        // the print dialog was opened.
1202
0
        sRange = OUString::number(mnInitialPage);
1203
0
    }
1204
0
    else if (nContent == 0)
1205
0
    {
1206
        // All pages
1207
0
        OUStringBuffer aBuf(32);
1208
0
        aBuf.append("1");
1209
0
        if (nPages > 1)
1210
0
            aBuf.append("-" + OUString::number(nPages));
1211
0
        sRange = aBuf.makeStringAndClear();
1212
0
    }
1213
0
    else
1214
0
    {
1215
0
        return;
1216
0
    }
1217
1218
0
    maPController->setValue(u"PageRange"_ustr, Any(sRange));
1219
0
}
1220
1221
void PrintDialog::updatePageSize(int nOrientation)
1222
0
{
1223
0
    VclPtr<Printer> aPrt(maPController->getPrinter());
1224
1225
0
    Size aSize;
1226
0
    if (mxNupPagesBox->get_active_id() == "1")
1227
0
    {
1228
0
        int nActive = mxPaperSizeBox->get_active();
1229
        // Custom entry is beyond GetPaperInfoCount(); use stored size.
1230
0
        if (mbHasCustomPaperEntry && nActive == aPrt->GetPaperInfoCount())
1231
0
        {
1232
0
            aSize = maCustomPaperSize;
1233
0
        }
1234
0
        else if (nActive >= 0 && nActive < aPrt->GetPaperInfoCount())
1235
0
        {
1236
0
            PaperInfo aInfo = aPrt->GetPaperInfo(nActive);
1237
0
            aSize = Size(aInfo.getWidth(), aInfo.getHeight());
1238
0
        }
1239
0
        if (aSize.IsEmpty())
1240
0
            aSize = aPrt->GetSizeOfPaper();
1241
1242
0
        if (nOrientation != ORIENTATION_AUTOMATIC)
1243
0
        {
1244
0
            if ((nOrientation == ORIENTATION_PORTRAIT && aSize.Width() > aSize.Height())
1245
0
                || (nOrientation == ORIENTATION_LANDSCAPE && aSize.Width() < aSize.Height()))
1246
0
            {
1247
                // coverity[swapped_arguments : FALSE] - this is in the intended order
1248
0
                aSize = Size(aSize.Height(), aSize.Width());
1249
0
            }
1250
0
        }
1251
0
    }
1252
0
    else
1253
0
        aSize = getJobPageSize();
1254
1255
0
    aPrt->SetPrintPageSize(aSize);
1256
0
    aPrt->SetUsePrintDialogSetting(nOrientation != ORIENTATION_AUTOMATIC);
1257
0
}
1258
1259
void PrintDialog::updateOrientationBox( const bool bAutomatic )
1260
0
{
1261
0
    if ( !bAutomatic )
1262
0
    {
1263
0
        Orientation eOrientation = maPController->getPrinter()->GetOrientation();
1264
0
        mxOrientationBox->set_active( static_cast<sal_Int32>(eOrientation) + 1 );
1265
0
    }
1266
0
    else if ( hasOrientationChanged() )
1267
0
    {
1268
0
        mxOrientationBox->set_active( ORIENTATION_AUTOMATIC );
1269
0
    }
1270
0
}
1271
1272
void PrintDialog::updatePaperSizeBox()
1273
0
{
1274
0
    VclPtr<Printer> aPrt( maPController->getPrinter() );
1275
1276
    // The custom entry lives beyond GetPaperInfoCount() and has no
1277
    // PaperInfo to match against, so leave it alone.
1278
0
    if (mbHasCustomPaperEntry && mxPaperSizeBox->get_active() == aPrt->GetPaperInfoCount())
1279
0
        return;
1280
1281
0
    mePaper = aPrt->GetPaper();
1282
0
    const Size aCurPaperSize(aPrt->GetSizeOfPaper());
1283
0
    PaperInfo aCurInfo(aCurPaperSize.getWidth(), aCurPaperSize.getHeight());
1284
0
    aCurInfo.doSloppyFit(true);
1285
1286
    // Dimensions first: a paper picked by document size may carry a Paper
1287
    // enum that matches some other entry, and matching on the enum would
1288
    // then select that wrong entry.
1289
0
    int nMatch = -1;
1290
0
    int nEnumMatch = -1;
1291
0
    for (int nPaper = 0; nPaper < aPrt->GetPaperInfoCount(); nPaper++)
1292
0
    {
1293
0
        PaperInfo aInfo = aPrt->GetPaperInfo(nPaper);
1294
0
        aInfo.doSloppyFit(true);
1295
1296
0
        if (!aCurPaperSize.IsEmpty() && aInfo.sloppyEqual(aCurInfo))
1297
0
        {
1298
0
            nMatch = nPaper;
1299
0
            break;
1300
0
        }
1301
0
        if (nEnumMatch == -1 && mePaper != PAPER_USER && aInfo.getPaper() == mePaper)
1302
0
            nEnumMatch = nPaper;
1303
0
    }
1304
1305
0
    if (nMatch == -1)
1306
0
        nMatch = nEnumMatch;
1307
0
    if (nMatch != -1)
1308
0
        mxPaperSizeBox->set_active(nMatch);
1309
0
}
1310
1311
bool PrintDialog::hasOrientationChanged() const
1312
0
{
1313
0
    const int nOrientation = mxOrientationBox->get_active();
1314
0
    const Orientation eOrientation = maPController->getPrinter()->GetOrientation();
1315
1316
0
    return (nOrientation == ORIENTATION_LANDSCAPE && eOrientation == Orientation::Portrait)
1317
0
        || (nOrientation == ORIENTATION_PORTRAIT && eOrientation == Orientation::Landscape);
1318
0
}
1319
1320
// Always use this function to set paper orientation to make sure everything behaves well
1321
void PrintDialog::setPaperOrientation( Orientation eOrientation, bool fromUser )
1322
0
{
1323
0
    VclPtr<Printer> aPrt( maPController->getPrinter() );
1324
0
    aPrt->SetOrientation( eOrientation );
1325
0
    maPController->setOrientationFromUser( eOrientation, fromUser );
1326
0
}
1327
1328
void PrintDialog::checkControlDependencies()
1329
0
{
1330
0
    if (mxCopyCountField->get_value() > 1)
1331
0
    {
1332
0
        mxCollateBox->set_sensitive( officecfg::VCL::VCLSettings::PrintDialog::Collate::isReadOnly() );
1333
0
        mxSingleJobsBox->set_sensitive( mxCollateBox->get_active() );
1334
0
    }
1335
0
    else
1336
0
    {
1337
0
        mxCollateBox->set_sensitive( false );
1338
0
        mxSingleJobsBox->set_sensitive( false );
1339
0
    }
1340
1341
0
    OUString aImg(mxCollateBox->get_active() ? SV_PRINT_COLLATE_BMP : SV_PRINT_NOCOLLATE_BMP);
1342
1343
0
    mxCollateImage->set_from_icon_name(aImg);
1344
1345
    // enable setup button only for printers that can be setup
1346
0
    bool bHaveSetup = maPController->getPrinter()->HasSupport( PrinterSupport::SetupDialog );
1347
0
    mxSetupButton->set_sensitive(bHaveSetup);
1348
0
}
1349
1350
void PrintDialog::checkOptionalControlDependencies()
1351
0
{
1352
0
    for( const auto& rEntry : maControlToPropertyMap )
1353
0
    {
1354
0
        assert(rEntry.first);
1355
1356
0
        bool bShouldbeEnabled = maPController->isUIOptionEnabled( rEntry.second );
1357
1358
0
        if (bShouldbeEnabled && dynamic_cast<weld::RadioButton*>(rEntry.first))
1359
0
        {
1360
0
            auto r_it = maControlToNumValMap.find( rEntry.first );
1361
0
            if( r_it != maControlToNumValMap.end() )
1362
0
            {
1363
0
                bShouldbeEnabled = maPController->isUIChoiceEnabled( rEntry.second, r_it->second );
1364
0
            }
1365
0
        }
1366
1367
0
        bool bIsEnabled = rEntry.first->get_sensitive();
1368
        // Enable does not do a change check first, so can be less cheap than expected
1369
0
        if (bShouldbeEnabled != bIsEnabled)
1370
0
            rEntry.first->set_sensitive( bShouldbeEnabled );
1371
0
    }
1372
0
}
1373
1374
void PrintDialog::initFromMultiPageSetup( const vcl::PrinterController::MultiPageSetup& i_rMPS )
1375
0
{
1376
0
    mxNupOrderWin->show();
1377
0
    mxPagesBtn->set_active(true);
1378
0
    mxBrochureBtn->hide();
1379
1380
    // setup field units for metric fields
1381
0
    const LocaleDataWrapper& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
1382
0
    FieldUnit eUnit = FieldUnit::MM;
1383
0
    sal_uInt16 nDigits = 0;
1384
0
    if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US )
1385
0
    {
1386
0
        eUnit = FieldUnit::INCH;
1387
0
        nDigits = 2;
1388
0
    }
1389
    // set units
1390
0
    mxPageMarginEdt->set_unit( eUnit );
1391
0
    mxSheetMarginEdt->set_unit( eUnit );
1392
1393
    // set precision
1394
0
    mxPageMarginEdt->set_digits( nDigits );
1395
0
    mxSheetMarginEdt->set_digits( nDigits );
1396
1397
0
    mxSheetMarginEdt->set_value( mxSheetMarginEdt->normalize( i_rMPS.nLeftMargin ), FieldUnit::MM_100TH );
1398
0
    mxPageMarginEdt->set_value( mxPageMarginEdt->normalize( i_rMPS.nHorizontalSpacing ), FieldUnit::MM_100TH );
1399
0
    mxBorderCB->set_active( i_rMPS.bDrawBorder );
1400
0
    mxNupRowsEdt->set_value( i_rMPS.nRows );
1401
0
    mxNupColEdt->set_value( i_rMPS.nColumns );
1402
0
    mxNupOrderBox->set_active( static_cast<sal_Int32>(i_rMPS.nOrder) );
1403
0
    if( i_rMPS.nRows != 1 || i_rMPS.nColumns != 1 )
1404
0
    {
1405
0
        mxNupPagesBox->set_active( mxNupPagesBox->get_count()-1 );
1406
0
        showAdvancedControls( true );
1407
0
        mxNupOrder->setValues( i_rMPS.nOrder, i_rMPS.nColumns, i_rMPS.nRows );
1408
0
    }
1409
0
}
1410
1411
void PrintDialog::updateNup( bool i_bMayUseCache )
1412
0
{
1413
0
    int nRows         = mxNupRowsEdt->get_value();
1414
0
    int nCols         = mxNupColEdt->get_value();
1415
0
    tools::Long nPageMargin  = mxPageMarginEdt->denormalize(mxPageMarginEdt->get_value( FieldUnit::MM_100TH ));
1416
0
    tools::Long nSheetMargin = mxSheetMarginEdt->denormalize(mxSheetMarginEdt->get_value( FieldUnit::MM_100TH ));
1417
1418
0
    PrinterController::MultiPageSetup aMPS;
1419
0
    aMPS.nRows         = nRows;
1420
0
    aMPS.nColumns      = nCols;
1421
0
    aMPS.nLeftMargin   =
1422
0
    aMPS.nTopMargin    =
1423
0
    aMPS.nRightMargin  =
1424
0
    aMPS.nBottomMargin = nSheetMargin;
1425
1426
0
    aMPS.nHorizontalSpacing =
1427
0
    aMPS.nVerticalSpacing   = nPageMargin;
1428
1429
0
    aMPS.bDrawBorder        = mxBorderCB->get_active();
1430
1431
0
    aMPS.nOrder = static_cast<NupOrderType>(mxNupOrderBox->get_active());
1432
1433
0
    int nOrientationMode = mxOrientationBox->get_active();
1434
0
    if( nOrientationMode == ORIENTATION_LANDSCAPE )
1435
0
        aMPS.aPaperSize = maNupLandscapeSize;
1436
0
    else if( nOrientationMode == ORIENTATION_PORTRAIT )
1437
0
        aMPS.aPaperSize = maNupPortraitSize;
1438
0
    else // automatic mode
1439
0
    {
1440
        // get size of first real page to see if it is portrait or landscape
1441
        // we assume same page sizes for all the pages for this
1442
0
        Size aPageSize = getJobPageSize();
1443
1444
0
        Size aMultiSize( aPageSize.Width() * nCols, aPageSize.Height() * nRows );
1445
0
        if( aMultiSize.Width() > aMultiSize.Height() ) // fits better on landscape
1446
0
        {
1447
0
            aMPS.aPaperSize = maNupLandscapeSize;
1448
0
            setPaperOrientation( Orientation::Landscape, false );
1449
0
        }
1450
0
        else
1451
0
        {
1452
0
            aMPS.aPaperSize = maNupPortraitSize;
1453
0
            setPaperOrientation( Orientation::Portrait, false );
1454
0
        }
1455
0
    }
1456
1457
0
    maPController->setMultipage( aMPS );
1458
1459
0
    mxNupOrder->setValues( aMPS.nOrder, nCols, nRows );
1460
1461
0
    schedulePreviewUpdate(i_bMayUseCache);
1462
0
}
1463
1464
void PrintDialog::updateNupFromPages( bool i_bMayUseCache )
1465
0
{
1466
0
    int nPages = mxNupPagesBox->get_active_id().toInt32();
1467
0
    int nRows   = mxNupRowsEdt->get_value();
1468
0
    int nCols   = mxNupColEdt->get_value();
1469
0
    tools::Long nPageMargin  = mxPageMarginEdt->denormalize(mxPageMarginEdt->get_value( FieldUnit::MM_100TH ));
1470
0
    tools::Long nSheetMargin = mxSheetMarginEdt->denormalize(mxSheetMarginEdt->get_value( FieldUnit::MM_100TH ));
1471
0
    bool bCustom = false;
1472
1473
0
    if( nPages == 1 )
1474
0
    {
1475
0
        nRows = nCols = 1;
1476
0
        nSheetMargin = 0;
1477
0
        nPageMargin = 0;
1478
0
    }
1479
0
    else if( nPages == 2 || nPages == 4 || nPages == 6 || nPages == 9 || nPages == 16 )
1480
0
    {
1481
0
        Size aJobPageSize( getJobPageSize() );
1482
0
        bool bPortrait = aJobPageSize.Width() < aJobPageSize.Height();
1483
0
        if( nPages == 2 )
1484
0
        {
1485
0
            if( bPortrait )
1486
0
            {
1487
0
                nRows = 1;
1488
0
                nCols = 2;
1489
0
            }
1490
0
            else
1491
0
            {
1492
0
                nRows = 2;
1493
0
                nCols = 1;
1494
0
            }
1495
0
        }
1496
0
        else if( nPages == 4 )
1497
0
            nRows = nCols = 2;
1498
0
        else if( nPages == 6 )
1499
0
        {
1500
0
            if( bPortrait )
1501
0
            {
1502
0
                nRows = 2;
1503
0
                nCols = 3;
1504
0
            }
1505
0
            else
1506
0
            {
1507
0
                nRows = 3;
1508
0
                nCols = 2;
1509
0
            }
1510
0
        }
1511
0
        else if( nPages == 9 )
1512
0
            nRows = nCols = 3;
1513
0
        else if( nPages == 16 )
1514
0
            nRows = nCols = 4;
1515
0
        nPageMargin = 0;
1516
0
        nSheetMargin = 0;
1517
0
    }
1518
0
    else
1519
0
        bCustom = true;
1520
1521
0
    if( nPages > 1 )
1522
0
    {
1523
        // set upper limits for margins based on job page size and rows/columns
1524
0
        Size aSize( getJobPageSize() );
1525
1526
        // maximum sheet distance: 1/2 sheet
1527
0
        tools::Long nHorzMax = aSize.Width()/2;
1528
0
        tools::Long nVertMax = aSize.Height()/2;
1529
0
        if( nSheetMargin > nHorzMax )
1530
0
            nSheetMargin = nHorzMax;
1531
0
        if( nSheetMargin > nVertMax )
1532
0
            nSheetMargin = nVertMax;
1533
1534
0
        mxSheetMarginEdt->set_max(
1535
0
                  mxSheetMarginEdt->normalize(
1536
0
                           std::min(nHorzMax, nVertMax) ), FieldUnit::MM_100TH );
1537
1538
        // maximum page distance
1539
0
        nHorzMax = (aSize.Width() - 2*nSheetMargin);
1540
0
        if( nCols > 1 )
1541
0
            nHorzMax /= (nCols-1);
1542
0
        nVertMax = (aSize.Height() - 2*nSheetMargin);
1543
0
        if( nRows > 1 )
1544
0
            nHorzMax /= (nRows-1);
1545
1546
0
        if( nPageMargin > nHorzMax )
1547
0
            nPageMargin = nHorzMax;
1548
0
        if( nPageMargin > nVertMax )
1549
0
            nPageMargin = nVertMax;
1550
1551
0
        mxPageMarginEdt->set_max(
1552
0
                 mxSheetMarginEdt->normalize(
1553
0
                           std::min(nHorzMax, nVertMax ) ), FieldUnit::MM_100TH );
1554
0
    }
1555
1556
0
    mxNupRowsEdt->set_value( nRows );
1557
0
    mxNupColEdt->set_value( nCols );
1558
0
    mxPageMarginEdt->set_value( mxPageMarginEdt->normalize( nPageMargin ), FieldUnit::MM_100TH );
1559
0
    mxSheetMarginEdt->set_value( mxSheetMarginEdt->normalize( nSheetMargin ), FieldUnit::MM_100TH );
1560
1561
0
    showAdvancedControls( bCustom );
1562
0
    updateNup( i_bMayUseCache );
1563
0
}
1564
1565
void PrintDialog::enableNupControls( bool bEnable )
1566
0
{
1567
0
    mxNupPagesBox->set_sensitive( bEnable );
1568
0
    mxNupNumPagesTxt->set_sensitive( bEnable );
1569
0
    mxNupColEdt->set_sensitive( bEnable );
1570
0
    mxNupTimesTxt->set_sensitive( bEnable );
1571
0
    mxNupRowsEdt->set_sensitive( bEnable );
1572
0
    mxPageMarginTxt1->set_sensitive( bEnable );
1573
0
    mxPageMarginEdt->set_sensitive( bEnable );
1574
0
    mxPageMarginTxt2->set_sensitive( bEnable );
1575
0
    mxSheetMarginTxt1->set_sensitive( bEnable );
1576
0
    mxSheetMarginEdt->set_sensitive( bEnable );
1577
0
    mxSheetMarginTxt2->set_sensitive( bEnable );
1578
0
    mxNupOrderTxt->set_sensitive( bEnable );
1579
0
    mxNupOrderBox->set_sensitive( bEnable );
1580
0
    mxNupOrderWin->set_sensitive( bEnable );
1581
0
    mxBorderCB->set_sensitive( bEnable );
1582
0
}
1583
1584
void PrintDialog::showAdvancedControls( bool i_bShow )
1585
0
{
1586
0
    mxNupNumPagesTxt->set_visible( i_bShow );
1587
0
    mxNupColEdt->set_visible( i_bShow );
1588
0
    mxNupTimesTxt->set_visible( i_bShow );
1589
0
    mxNupRowsEdt->set_visible( i_bShow );
1590
0
    mxPageMarginTxt1->set_visible( i_bShow );
1591
0
    mxPageMarginEdt->set_visible( i_bShow );
1592
0
    mxPageMarginTxt2->set_visible( i_bShow );
1593
0
    mxSheetMarginTxt1->set_visible( i_bShow );
1594
0
    mxSheetMarginEdt->set_visible( i_bShow );
1595
0
    mxSheetMarginTxt2->set_visible( i_bShow );
1596
0
}
1597
1598
namespace
1599
{
1600
    void setHelpId( weld::Widget* i_pWindow, const Sequence< OUString >& i_rHelpIds, sal_Int32 i_nIndex )
1601
0
    {
1602
0
        if( i_nIndex >= 0 && i_nIndex < i_rHelpIds.getLength() )
1603
0
            i_pWindow->set_help_id( i_rHelpIds.getConstArray()[i_nIndex] );
1604
0
    }
1605
1606
    void setHelpText( weld::Widget* i_pWindow, const Sequence< OUString >& i_rHelpTexts, sal_Int32 i_nIndex )
1607
0
    {
1608
        // without a help text set and the correct smartID,
1609
        // help texts will be retrieved from the online help system
1610
0
        if( i_nIndex >= 0 && i_nIndex < i_rHelpTexts.getLength() )
1611
0
            i_pWindow->set_tooltip_text(i_rHelpTexts.getConstArray()[i_nIndex]);
1612
0
    }
1613
}
1614
1615
void PrintDialog::setupOptionalUI()
1616
0
{
1617
0
    const Sequence< PropertyValue >& rOptions( maPController->getUIOptions() );
1618
0
    for( const auto& rOption : rOptions )
1619
0
    {
1620
0
        if (rOption.Name == "OptionsUIFile")
1621
0
        {
1622
0
            OUString sOptionsUIFile;
1623
0
            rOption.Value >>= sOptionsUIFile;
1624
0
            mxCustomOptionsUIBuilder = Application::CreateBuilder(mxCustom.get(), sOptionsUIFile);
1625
0
            std::unique_ptr<weld::Container> xWindow = mxCustomOptionsUIBuilder->weld_container(u"box"_ustr);
1626
0
            xWindow->set_help_id(u"vcl/ui/printdialog/PrintDialog"_ustr);
1627
0
            xWindow->show();
1628
0
            continue;
1629
0
        }
1630
1631
0
        Sequence< beans::PropertyValue > aOptProp;
1632
0
        rOption.Value >>= aOptProp;
1633
1634
        // extract ui element
1635
0
        OUString aCtrlType;
1636
0
        OUString aID;
1637
0
        OUString aText;
1638
0
        OUString aPropertyName;
1639
0
        Sequence< OUString > aChoices;
1640
0
        Sequence< sal_Bool > aChoicesDisabled;
1641
0
        Sequence< OUString > aHelpTexts;
1642
0
        Sequence< OUString > aIDs;
1643
0
        Sequence< OUString > aHelpIds;
1644
0
        sal_Int64 nMinValue = 0, nMaxValue = 0;
1645
0
        OUString aGroupingHint;
1646
1647
0
        for (const beans::PropertyValue& rEntry : aOptProp)
1648
0
        {
1649
0
            if ( rEntry.Name == "ID" )
1650
0
            {
1651
0
                rEntry.Value >>= aIDs;
1652
0
                aID = aIDs[0];
1653
0
            }
1654
0
            if ( rEntry.Name == "Text" )
1655
0
            {
1656
0
                rEntry.Value >>= aText;
1657
0
            }
1658
0
            else if ( rEntry.Name == "ControlType" )
1659
0
            {
1660
0
                rEntry.Value >>= aCtrlType;
1661
0
            }
1662
0
            else if ( rEntry.Name == "Choices" )
1663
0
            {
1664
0
                rEntry.Value >>= aChoices;
1665
0
            }
1666
0
            else if ( rEntry.Name == "ChoicesDisabled" )
1667
0
            {
1668
0
                rEntry.Value >>= aChoicesDisabled;
1669
0
            }
1670
0
            else if ( rEntry.Name == "Property" )
1671
0
            {
1672
0
                PropertyValue aVal;
1673
0
                rEntry.Value >>= aVal;
1674
0
                aPropertyName = aVal.Name;
1675
0
            }
1676
0
            else if ( rEntry.Name == "Enabled" )
1677
0
            {
1678
0
            }
1679
0
            else if ( rEntry.Name == "GroupingHint" )
1680
0
            {
1681
0
                rEntry.Value >>= aGroupingHint;
1682
0
            }
1683
0
            else if ( rEntry.Name == "DependsOnName" )
1684
0
            {
1685
0
            }
1686
0
            else if ( rEntry.Name == "DependsOnEntry" )
1687
0
            {
1688
0
            }
1689
0
            else if ( rEntry.Name == "AttachToDependency" )
1690
0
            {
1691
0
            }
1692
0
            else if ( rEntry.Name == "MinValue" )
1693
0
            {
1694
0
                rEntry.Value >>= nMinValue;
1695
0
            }
1696
0
            else if ( rEntry.Name == "MaxValue" )
1697
0
            {
1698
0
                rEntry.Value >>= nMaxValue;
1699
0
            }
1700
0
            else if ( rEntry.Name == "HelpText" )
1701
0
            {
1702
0
                if( ! (rEntry.Value >>= aHelpTexts) )
1703
0
                {
1704
0
                    OUString aHelpText;
1705
0
                    if( rEntry.Value >>= aHelpText )
1706
0
                    {
1707
0
                        aHelpTexts.realloc( 1 );
1708
0
                        *aHelpTexts.getArray() = aHelpText;
1709
0
                    }
1710
0
                }
1711
0
            }
1712
0
            else if ( rEntry.Name == "HelpId" )
1713
0
            {
1714
0
                if( ! (rEntry.Value >>= aHelpIds ) )
1715
0
                {
1716
0
                    OUString aHelpId;
1717
0
                    if( rEntry.Value >>= aHelpId )
1718
0
                    {
1719
0
                        aHelpIds.realloc( 1 );
1720
0
                        *aHelpIds.getArray() = aHelpId;
1721
0
                    }
1722
0
                }
1723
0
            }
1724
0
            else if ( rEntry.Name == "HintNoLayoutPage" )
1725
0
            {
1726
0
                bool bHasLayoutFrame = false;
1727
0
                rEntry.Value >>= bHasLayoutFrame;
1728
0
                mbShowLayoutFrame = !bHasLayoutFrame;
1729
0
            }
1730
0
        }
1731
1732
0
        if (aCtrlType == "Group")
1733
0
        {
1734
0
            aID = "custom";
1735
1736
0
            weld::Container* pPage = mxTabCtrl->get_page(aID);
1737
0
            if (!pPage)
1738
0
                continue;
1739
1740
0
            mxTabCtrl->set_tab_label_text(aID, aText);
1741
1742
            // set help id
1743
0
            if (aHelpIds.hasElements())
1744
0
                pPage->set_help_id(aHelpIds[0]);
1745
1746
            // set help text
1747
0
            if (aHelpTexts.hasElements())
1748
0
                pPage->set_tooltip_text(aHelpTexts[0]);
1749
1750
0
            pPage->show();
1751
0
        }
1752
0
        else if (aCtrlType == "Subgroup" && !aID.isEmpty())
1753
0
        {
1754
0
            std::unique_ptr<weld::Widget> xWidget;
1755
            // since 'New Print Dialog Design' fromwhich in calc is not a frame anymore
1756
0
            if (aID == "fromwhich")
1757
0
            {
1758
0
                std::unique_ptr<weld::Label> xLabel = m_xBuilder->weld_label(aID);
1759
0
                xLabel->set_label(aText);
1760
0
                xWidget = std::move(xLabel);
1761
0
            }
1762
0
            else
1763
0
            {
1764
0
                std::unique_ptr<weld::Frame> xFrame = m_xBuilder->weld_frame(aID);
1765
0
                if (!xFrame && mxCustomOptionsUIBuilder)
1766
0
                    xFrame = mxCustomOptionsUIBuilder->weld_frame(aID);
1767
0
                if (xFrame)
1768
0
                {
1769
0
                    xFrame->set_label(aText);
1770
0
                    xWidget = std::move(xFrame);
1771
0
                }
1772
0
            }
1773
1774
0
            if (!xWidget)
1775
0
                continue;
1776
1777
            // set help id
1778
0
            setHelpId(xWidget.get(), aHelpIds, 0);
1779
            // set help text
1780
0
            setHelpText(xWidget.get(), aHelpTexts, 0);
1781
1782
0
            xWidget->show();
1783
0
        }
1784
        // EVIL
1785
0
        else if( aCtrlType == "Bool" && aGroupingHint == "LayoutPage" && aPropertyName == "PrintProspect" )
1786
0
        {
1787
0
            mxBrochureBtn->set_label(aText);
1788
0
            mxBrochureBtn->show();
1789
1790
0
            bool bVal = false;
1791
0
            PropertyValue* pVal = maPController->getValue( aPropertyName );
1792
0
            if( pVal )
1793
0
                pVal->Value >>= bVal;
1794
0
            mxBrochureBtn->set_active( bVal );
1795
0
            mxBrochureBtn->set_sensitive( maPController->isUIOptionEnabled( aPropertyName ) && pVal != nullptr );
1796
1797
0
            maPropertyToWindowMap[aPropertyName].emplace_back(mxBrochureBtn.get());
1798
0
            maControlToPropertyMap[mxBrochureBtn.get()] = aPropertyName;
1799
1800
            // set help id
1801
0
            setHelpId( mxBrochureBtn.get(), aHelpIds, 0 );
1802
            // set help text
1803
0
            setHelpText( mxBrochureBtn.get(), aHelpTexts, 0 );
1804
0
        }
1805
0
        else if (aCtrlType == "Bool")
1806
0
        {
1807
            // add a check box
1808
0
            std::unique_ptr<weld::CheckButton> xNewBox = m_xBuilder->weld_check_button(aID);
1809
0
            if (!xNewBox && mxCustomOptionsUIBuilder)
1810
0
                xNewBox = mxCustomOptionsUIBuilder->weld_check_button(aID);
1811
0
            if (!xNewBox)
1812
0
                continue;
1813
1814
0
            xNewBox->set_label( aText );
1815
0
            xNewBox->show();
1816
1817
0
            bool bVal = false;
1818
0
            PropertyValue* pVal = maPController->getValue( aPropertyName );
1819
0
            if( pVal )
1820
0
                pVal->Value >>= bVal;
1821
0
            xNewBox->set_active( bVal );
1822
0
            xNewBox->connect_toggled( LINK( this, PrintDialog, UIOption_CheckHdl ) );
1823
1824
0
            maExtraControls.emplace_back(std::move(xNewBox));
1825
1826
0
            weld::Widget* pWidget = maExtraControls.back().get();
1827
1828
0
            maPropertyToWindowMap[aPropertyName].emplace_back(pWidget);
1829
0
            maControlToPropertyMap[pWidget] = aPropertyName;
1830
1831
            // set help id
1832
0
            setHelpId(pWidget, aHelpIds, 0);
1833
            // set help text
1834
0
            setHelpText(pWidget, aHelpTexts, 0);
1835
0
        }
1836
0
        else if (aCtrlType == "Radio")
1837
0
        {
1838
0
            sal_Int32 nCurHelpText = 0;
1839
1840
            // iterate options
1841
0
            sal_Int32 nSelectVal = 0;
1842
0
            PropertyValue* pVal = maPController->getValue( aPropertyName );
1843
0
            if( pVal && pVal->Value.hasValue() )
1844
0
                pVal->Value >>= nSelectVal;
1845
0
            for( sal_Int32 m = 0; m < aChoices.getLength(); m++ )
1846
0
            {
1847
0
                aID = aIDs[m];
1848
0
                std::unique_ptr<weld::RadioButton> xBtn = m_xBuilder->weld_radio_button(aID);
1849
0
                if (!xBtn && mxCustomOptionsUIBuilder)
1850
0
                    xBtn = mxCustomOptionsUIBuilder->weld_radio_button(aID);
1851
0
                if (!xBtn)
1852
0
                    continue;
1853
1854
0
                xBtn->set_label( aChoices[m] );
1855
0
                xBtn->set_active( m == nSelectVal );
1856
0
                xBtn->connect_toggled( LINK( this, PrintDialog, UIOption_RadioHdl ) );
1857
0
                if( aChoicesDisabled.getLength() > m && aChoicesDisabled[m] )
1858
0
                    xBtn->set_sensitive( false );
1859
0
                xBtn->show();
1860
1861
0
                maExtraControls.emplace_back(std::move(xBtn));
1862
1863
0
                weld::Widget* pWidget = maExtraControls.back().get();
1864
1865
0
                maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
1866
0
                maControlToPropertyMap[pWidget] = aPropertyName;
1867
0
                maControlToNumValMap[pWidget] = m;
1868
1869
                // set help id
1870
0
                setHelpId( pWidget, aHelpIds, nCurHelpText );
1871
                // set help text
1872
0
                setHelpText( pWidget, aHelpTexts, nCurHelpText );
1873
0
                nCurHelpText++;
1874
0
            }
1875
0
        }
1876
0
        else if ( aCtrlType == "List" )
1877
0
        {
1878
0
            std::unique_ptr<weld::ComboBox> xList = m_xBuilder->weld_combo_box(aID);
1879
0
            if (!xList && mxCustomOptionsUIBuilder)
1880
0
                xList = mxCustomOptionsUIBuilder->weld_combo_box(aID);
1881
0
            if (!xList)
1882
0
                continue;
1883
1884
            // iterate options
1885
0
            for (const auto& rChoice : aChoices)
1886
0
                xList->append_text(rChoice);
1887
1888
0
            sal_Int32 nSelectVal = 0;
1889
0
            PropertyValue* pVal = maPController->getValue( aPropertyName );
1890
0
            if( pVal && pVal->Value.hasValue() )
1891
0
                pVal->Value >>= nSelectVal;
1892
0
            xList->set_active(nSelectVal);
1893
0
            xList->connect_changed( LINK( this, PrintDialog, UIOption_SelectHdl ) );
1894
0
            xList->show();
1895
1896
0
            maExtraControls.emplace_back(std::move(xList));
1897
1898
0
            weld::Widget* pWidget = maExtraControls.back().get();
1899
1900
0
            maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
1901
0
            maControlToPropertyMap[pWidget] = aPropertyName;
1902
1903
            // set help id
1904
0
            setHelpId( pWidget, aHelpIds, 0 );
1905
            // set help text
1906
0
            setHelpText( pWidget, aHelpTexts, 0 );
1907
0
        }
1908
0
        else if ( aCtrlType == "Range" )
1909
0
        {
1910
0
            std::unique_ptr<weld::SpinButton> xField = m_xBuilder->weld_spin_button(aID);
1911
0
            if (!xField && mxCustomOptionsUIBuilder)
1912
0
                xField = mxCustomOptionsUIBuilder->weld_spin_button(aID);
1913
0
            if (!xField)
1914
0
                continue;
1915
1916
            // set min/max and current value
1917
0
            if(nMinValue != nMaxValue)
1918
0
                xField->set_range(nMinValue, nMaxValue);
1919
1920
0
            sal_Int64 nCurVal = 0;
1921
0
            PropertyValue* pVal = maPController->getValue( aPropertyName );
1922
0
            if( pVal && pVal->Value.hasValue() )
1923
0
                pVal->Value >>= nCurVal;
1924
0
            xField->set_value( nCurVal );
1925
0
            xField->connect_value_changed( LINK( this, PrintDialog, UIOption_SpinModifyHdl ) );
1926
0
            xField->show();
1927
1928
0
            maExtraControls.emplace_back(std::move(xField));
1929
1930
0
            weld::Widget* pWidget = maExtraControls.back().get();
1931
1932
0
            maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
1933
0
            maControlToPropertyMap[pWidget] = aPropertyName;
1934
1935
            // set help id
1936
0
            setHelpId( pWidget, aHelpIds, 0 );
1937
            // set help text
1938
0
            setHelpText( pWidget, aHelpTexts, 0 );
1939
0
        }
1940
0
        else if (aCtrlType == "Edit")
1941
0
        {
1942
0
            std::unique_ptr<weld::Entry> xField = m_xBuilder->weld_entry(aID);
1943
0
            if (!xField && mxCustomOptionsUIBuilder)
1944
0
                xField = mxCustomOptionsUIBuilder->weld_entry(aID);
1945
0
            if (!xField)
1946
0
                continue;
1947
1948
0
            OUString aCurVal;
1949
0
            PropertyValue* pVal = maPController->getValue( aPropertyName );
1950
0
            if( pVal && pVal->Value.hasValue() )
1951
0
                pVal->Value >>= aCurVal;
1952
0
            if (aPropertyName != "PageRange")
1953
0
            {
1954
0
                xField->set_text( aCurVal );
1955
0
            }
1956
0
            xField->connect_changed( LINK( this, PrintDialog, UIOption_EntryModifyHdl ) );
1957
0
            xField->show();
1958
1959
0
            maExtraControls.emplace_back(std::move(xField));
1960
1961
0
            weld::Widget* pWidget = maExtraControls.back().get();
1962
1963
0
            maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
1964
0
            maControlToPropertyMap[pWidget] = aPropertyName;
1965
1966
            // set help id
1967
0
            setHelpId( pWidget, aHelpIds, 0 );
1968
            // set help text
1969
0
            setHelpText( pWidget, aHelpTexts, 0 );
1970
0
        }
1971
0
        else
1972
0
        {
1973
0
            SAL_WARN( "vcl", "Unsupported UI option: \"" << aCtrlType << '"');
1974
0
        }
1975
0
    }
1976
1977
    // #i106506# if no brochure button, then the singular Pages radio button
1978
    // makes no sense, so replace it by a FixedText label
1979
0
    if (!mxBrochureBtn->get_visible() && mxPagesBtn->get_visible())
1980
0
    {
1981
0
        mxPagesBoxTitleTxt->set_label(mxPagesBtn->get_label());
1982
0
        mxPagesBoxTitleTxt->show();
1983
0
        mxPagesBtn->hide();
1984
1985
0
        mxNupPagesBox->set_accessible_relation_labeled_by(mxPagesBoxTitleTxt.get());
1986
0
    }
1987
1988
    // update enable states
1989
0
    checkOptionalControlDependencies();
1990
1991
    // print range not shown (currently math only) -> hide spacer line and reverse order
1992
0
    if (!mxPageRangeEdit->get_visible())
1993
0
    {
1994
0
        mxReverseOrderBox->hide();
1995
0
    }
1996
1997
0
    if (!mxCustomOptionsUIBuilder)
1998
0
        mxTabCtrl->remove_page(mxTabCtrl->get_page_ident(1));
1999
0
}
2000
2001
void PrintDialog::makeEnabled( weld::Widget* i_pWindow )
2002
0
{
2003
0
    auto it = maControlToPropertyMap.find( i_pWindow );
2004
0
    if( it != maControlToPropertyMap.end() )
2005
0
    {
2006
0
        OUString aDependency( maPController->makeEnabled( it->second ) );
2007
0
        if( !aDependency.isEmpty() )
2008
0
            updateWindowFromProperty( aDependency );
2009
0
    }
2010
0
}
2011
2012
void PrintDialog::updateWindowFromProperty( const OUString& i_rProperty )
2013
0
{
2014
0
    beans::PropertyValue* pValue = maPController->getValue( i_rProperty );
2015
0
    auto it = maPropertyToWindowMap.find( i_rProperty );
2016
0
    if( !(pValue && it != maPropertyToWindowMap.end()) )
2017
0
        return;
2018
2019
0
    const auto& rWindows( it->second );
2020
0
    if(  rWindows.empty() )
2021
0
        return;
2022
2023
0
    bool bVal = false;
2024
0
    sal_Int32 nVal = -1;
2025
0
    if( pValue->Value >>= bVal )
2026
0
    {
2027
        // we should have a CheckBox for this one
2028
0
        weld::CheckButton* pBox = dynamic_cast<weld::CheckButton*>(rWindows.front());
2029
0
        if( pBox )
2030
0
        {
2031
0
            pBox->set_active( bVal );
2032
0
        }
2033
0
        else if ( i_rProperty == "PrintProspect" )
2034
0
        {
2035
            // EVIL special case
2036
0
            if( bVal )
2037
0
                mxBrochureBtn->set_active(true);
2038
0
            else
2039
0
                mxPagesBtn->set_active(true);
2040
0
        }
2041
0
        else
2042
0
        {
2043
0
            SAL_WARN( "vcl", "missing a checkbox" );
2044
0
        }
2045
0
    }
2046
0
    else if( pValue->Value >>= nVal )
2047
0
    {
2048
        // this could be a ListBox or a RadioButtonGroup
2049
0
        weld::ComboBox* pList = dynamic_cast<weld::ComboBox*>(rWindows.front());
2050
0
        if( pList )
2051
0
        {
2052
0
            pList->set_active( static_cast< sal_uInt16 >(nVal) );
2053
0
        }
2054
0
        else if( nVal >= 0 && o3tl::make_unsigned(nVal) < rWindows.size() )
2055
0
        {
2056
0
            weld::RadioButton* pBtn = dynamic_cast<weld::RadioButton*>(rWindows[nVal]);
2057
0
            SAL_WARN_IF( !pBtn, "vcl", "unexpected control for property" );
2058
0
            if( pBtn )
2059
0
                pBtn->set_active(true);
2060
0
        }
2061
0
    }
2062
0
}
2063
2064
bool PrintDialog::isPrintToFile() const
2065
0
{
2066
0
    return ( mxPrinters->get_active() == 0 );
2067
0
}
2068
2069
bool PrintDialog::isCollate() const
2070
0
{
2071
0
    return mxCopyCountField->get_value() > 1 && mxCollateBox->get_active();
2072
0
}
2073
2074
bool PrintDialog::isSingleJobs() const
2075
0
{
2076
0
    return mxSingleJobsBox->get_active();
2077
0
}
2078
2079
bool PrintDialog::hasPreview() const
2080
0
{
2081
0
    return mxPreviewBox->get_active();
2082
0
}
2083
2084
PropertyValue* PrintDialog::getValueForWindow( weld::Widget* i_pWindow ) const
2085
0
{
2086
0
    PropertyValue* pVal = nullptr;
2087
0
    auto it = maControlToPropertyMap.find( i_pWindow );
2088
0
    if( it != maControlToPropertyMap.end() )
2089
0
    {
2090
0
        pVal = maPController->getValue( it->second );
2091
0
        SAL_WARN_IF( !pVal, "vcl", "property value not found" );
2092
0
    }
2093
0
    else
2094
0
    {
2095
0
        OSL_FAIL( "changed control not in property map" );
2096
0
    }
2097
0
    return pVal;
2098
0
}
2099
2100
IMPL_LINK_NOARG(PrintDialog, TogglePreviewHdl, weld::Toggleable&, void)
2101
0
{
2102
0
    schedulePreviewUpdate(true);
2103
0
}
2104
2105
IMPL_LINK_NOARG(PrintDialog, ToggleBorderHdl, weld::Toggleable&, void)
2106
0
{
2107
0
    updateNup();
2108
0
}
2109
2110
IMPL_LINK_NOARG(PrintDialog, ToggleSingleJobsHdl, weld::Toggleable&, void)
2111
0
{
2112
0
    maPController->setValue(u"SinglePrintJobs"_ustr, Any(isSingleJobs()));
2113
0
    checkControlDependencies();
2114
0
}
2115
2116
IMPL_LINK_NOARG(PrintDialog, ToggleCollateHdl, weld::Toggleable&, void)
2117
0
{
2118
0
    maPController->setValue(u"Collate"_ustr, Any(isCollate()));
2119
0
    checkControlDependencies();
2120
0
}
2121
2122
IMPL_LINK_NOARG(PrintDialog, ToggleReverseOrderHdl, weld::Toggleable&, void)
2123
0
{
2124
0
    bool bChecked = mxReverseOrderBox->get_active();
2125
0
    maPController->setReversePrint(bChecked);
2126
0
    maPController->setValue(u"PrintReverse"_ustr, Any(bChecked));
2127
0
    schedulePreviewUpdate(true);
2128
0
}
2129
2130
IMPL_LINK_NOARG(PrintDialog, ToggleBrochureHdl, weld::Toggleable&, void)
2131
0
{
2132
0
    PropertyValue* pVal = getValueForWindow(mxBrochureBtn.get());
2133
0
    if (pVal)
2134
0
    {
2135
0
        bool bVal = mxBrochureBtn->get_active();
2136
0
        pVal->Value <<= bVal;
2137
2138
0
        checkOptionalControlDependencies();
2139
2140
        // update preview and page settings
2141
0
        schedulePreviewUpdate(false);
2142
0
    }
2143
0
    if (mxBrochureBtn->get_active())
2144
0
    {
2145
0
        mxOrientationBox->set_sensitive(false);
2146
0
        mxOrientationBox->set_active(ORIENTATION_LANDSCAPE);
2147
0
        mxNupPagesBox->set_active(0);
2148
0
        updateNupFromPages();
2149
0
        showAdvancedControls(false);
2150
0
        enableNupControls(false);
2151
0
    }
2152
0
    else
2153
0
    {
2154
0
        mxOrientationBox->set_sensitive(true);
2155
0
        mxOrientationBox->set_active(ORIENTATION_AUTOMATIC);
2156
0
        updatePageSize(mxOrientationBox->get_active());
2157
0
        enableNupControls(true);
2158
0
        updateNupFromPages();
2159
0
    }
2160
0
}
2161
2162
IMPL_LINK(PrintDialog, ClickOKCancelHdl, weld::Button&, rButton, void)
2163
0
{
2164
0
    storeToSettings();
2165
0
    m_xDialog->response(&rButton == mxOKButton.get() ? RET_OK : RET_CANCEL);
2166
0
}
2167
2168
IMPL_LINK_NOARG(PrintDialog, ClickForwardHdl, weld::Button&, void)
2169
0
{
2170
0
    previewForward();
2171
0
}
2172
2173
IMPL_LINK_NOARG(PrintDialog, ClickBackwardHdl, weld::Button&, void)
2174
0
{
2175
0
    previewBackward();
2176
0
}
2177
2178
IMPL_LINK_NOARG(PrintDialog, ClickFirstHdl, weld::Button&, void)
2179
0
{
2180
0
    previewFirst();
2181
0
}
2182
2183
IMPL_LINK_NOARG(PrintDialog, ClickLastHdl, weld::Button&, void)
2184
0
{
2185
0
    previewLast();
2186
0
}
2187
2188
IMPL_LINK_NOARG(PrintDialog, ClickSetupHdl, weld::Button&, void)
2189
0
{
2190
0
    const Size aSavedCustomSize = maCustomPaperSize;
2191
0
    const bool bHadCustomEntry = mbHasCustomPaperEntry;
2192
    // Save orientation dropdown — Properties dialog can clobber it.
2193
0
    const int nSavedOrient = mxOrientationBox->get_active();
2194
2195
0
    maPController->setupPrinter(m_xDialog.get());
2196
2197
0
    if (!isPrintToFile())
2198
0
    {
2199
        // Rebuild paper size list to restore custom entry if needed.
2200
0
        setPaperSizes();
2201
2202
        // Restore lost custom entry from saved document page size.
2203
0
        if (bHadCustomEntry && !mbHasCustomPaperEntry
2204
0
            && !aSavedCustomSize.IsEmpty())
2205
0
        {
2206
0
            maFirstPageSize = aSavedCustomSize;
2207
0
            setPaperSizes();
2208
0
        }
2209
2210
        // tdf#173642: setPaperSizes() selects the paper matching the document,
2211
        // which would discard a paper picked in the dropdown.
2212
0
        updatePaperSizeBox();
2213
0
    }
2214
2215
    // Sync maSelectedPaperSize with current combobox selection.
2216
0
    if (mxPaperSizeBox->get_sensitive() && mxPaperSizeBox->get_active() >= 0)
2217
0
        SelectPaperSizeHdl(*mxPaperSizeBox);
2218
2219
0
    updateOrientationBox(false);
2220
2221
    // Restore orientation dropdown — Properties dialog may have
2222
    // clobbered the printer's orientation as a side effect.
2223
0
    if (mxOrientationBox->get_active() != nSavedOrient)
2224
0
    {
2225
0
        mxOrientationBox->set_active(nSavedOrient);
2226
0
    }
2227
2228
0
    updatePageSize(mxOrientationBox->get_active());
2229
2230
0
    setupPaperSidesBox();
2231
2232
    // tdf#63905 don't use cache: page size may change
2233
0
    schedulePreviewUpdate(false);
2234
0
    checkControlDependencies();
2235
0
}
2236
2237
IMPL_LINK_NOARG( PrintDialog, SelectPrinterHdl, weld::ComboBox&, void )
2238
0
{
2239
    // Save custom size — setPrinter() resets mbPapersizeFromUser.
2240
0
    const Size aSavedCustomSize = maCustomPaperSize;
2241
0
    const bool bHadCustomEntry = mbHasCustomPaperEntry;
2242
2243
0
    if (!isPrintToFile())
2244
0
    {
2245
0
        OUString aNewPrinter(mxPrinters->get_active_text());
2246
        // set new printer
2247
0
        maPController->setPrinter(VclPtrInstance<Printer>(aNewPrinter));
2248
0
        maPController->resetPrinterOptions(false);
2249
        // invalidate page cache and start fresh
2250
0
        maPController->invalidatePageCache();
2251
0
        maFirstPageSize = Size();
2252
2253
0
        updateOrientationBox();
2254
2255
        // update text fields
2256
0
        mxOKButton->set_label(maPrintText);
2257
0
        updatePrinterText();
2258
0
        updateNup(false);
2259
0
        setPaperSizes();
2260
2261
        // Restore lost custom entry from saved document page size.
2262
0
        if (bHadCustomEntry && !mbHasCustomPaperEntry
2263
0
            && !aSavedCustomSize.IsEmpty())
2264
0
        {
2265
0
            maCustomPaperSize = aSavedCustomSize;
2266
0
            maFirstPageSize = aSavedCustomSize;
2267
0
            setPaperSizes();
2268
0
        }
2269
2270
0
        schedulePreviewUpdate(true);
2271
0
    }
2272
0
    else // print to file
2273
0
    {
2274
        // use the default printer or FIXME: the last used one?
2275
0
        maPController->setPrinter(VclPtrInstance<Printer>(Printer::GetDefaultPrinterName()));
2276
0
        mxOKButton->set_label(maPrintToFileText);
2277
0
        maPController->resetPrinterOptions(true);
2278
2279
0
        setPaperSizes();
2280
2281
0
        if (bHadCustomEntry && !mbHasCustomPaperEntry
2282
0
            && !aSavedCustomSize.IsEmpty())
2283
0
        {
2284
0
            maCustomPaperSize = aSavedCustomSize;
2285
0
            maFirstPageSize = aSavedCustomSize;
2286
0
            setPaperSizes();
2287
0
        }
2288
2289
0
        updateOrientationBox();
2290
0
        schedulePreviewUpdate(true);
2291
0
    }
2292
2293
0
    setupPaperSidesBox();
2294
2295
    // Related: tdf#159995 changing a listbox's active item does not
2296
    // trigger an update to the preview so force an update to the
2297
    // preview by calling each active listbox's select handler after
2298
    // changing the active printer.
2299
0
    if (mxOrientationBox->get_sensitive())
2300
0
        SelectOrientationHdl(*mxOrientationBox);
2301
0
    if (mxPaperSizeBox->get_sensitive())
2302
0
        SelectPaperSizeHdl(*mxPaperSizeBox);
2303
0
}
2304
2305
IMPL_LINK_NOARG(PrintDialog, SelectPaperSidesHdl, weld::ComboBox&, void)
2306
0
{
2307
0
    DuplexMode eDuplex = static_cast<DuplexMode>(mxPaperSidesBox->get_active() + 1);
2308
0
    maPController->getPrinter()->SetDuplexMode(eDuplex);
2309
0
}
2310
2311
IMPL_LINK_NOARG(PrintDialog, SelectOrientationHdl, weld::ComboBox&, void)
2312
0
{
2313
0
    int nOrientation = mxOrientationBox->get_active();
2314
0
    if (nOrientation != ORIENTATION_AUTOMATIC)
2315
0
        setPaperOrientation(static_cast<Orientation>(nOrientation - 1), true);
2316
2317
0
    updatePageSize(nOrientation);
2318
0
    updateNup(false);
2319
0
}
2320
2321
IMPL_LINK_NOARG(PrintDialog, SelectNupOrderHdl, weld::ComboBox&, void)
2322
0
{
2323
0
    updateNup();
2324
0
}
2325
2326
IMPL_LINK_NOARG(PrintDialog, SelectNupPagesHdl, weld::ComboBox&, void)
2327
0
{
2328
0
    if (!mxPagesBtn->get_active())
2329
0
        mxPagesBtn->set_active(true);
2330
0
    updatePageSize(mxOrientationBox->get_active());
2331
0
    updateNupFromPages(false);
2332
0
}
2333
2334
IMPL_LINK_NOARG(PrintDialog, SelectPaperSizeHdl, weld::ComboBox&, void)
2335
0
{
2336
0
    VclPtr<Printer> aPrt(maPController->getPrinter());
2337
0
    int nActive = mxPaperSizeBox->get_active();
2338
2339
    // Save orientation — SetPaper/SetPaperSizeUser can clobber it.
2340
0
    Orientation eOrientBefore = aPrt->GetOrientation();
2341
2342
    // Custom entry is beyond GetPaperInfoCount(); handle separately.
2343
0
    if (mbHasCustomPaperEntry && nActive == aPrt->GetPaperInfoCount())
2344
0
    {
2345
0
        mePaper = PAPER_USER;
2346
        // SetPaperSizeUser needs logical units; set Map100thMM.
2347
0
        {
2348
0
            auto popIt = aPrt->ScopedPush();
2349
0
            aPrt->SetMapMode(MapMode(MapUnit::Map100thMM));
2350
0
            aPrt->SetPaperSizeUser(maCustomPaperSize);
2351
0
        }
2352
0
        maPController->setPaperSizeFromUser(maCustomPaperSize);
2353
0
        maSelectedPaperSize = maCustomPaperSize;
2354
0
    }
2355
0
    else
2356
0
    {
2357
0
        PaperInfo aInfo = aPrt->GetPaperInfo(nActive);
2358
0
        aInfo.doSloppyFit(true);
2359
0
        mePaper = aInfo.getPaper();
2360
0
        Size aSelectedSize(aInfo.getWidth(), aInfo.getHeight());
2361
0
        if (mePaper == PAPER_USER)
2362
0
            aPrt->SetPaperSizeUser(aSelectedSize);
2363
0
        else
2364
0
            aPrt->SetPaper(mePaper);
2365
2366
0
        maPController->setPaperSizeFromUser(aSelectedSize);
2367
0
        maSelectedPaperSize = aSelectedSize;
2368
0
    }
2369
2370
    // In Automatic mode, restore orientation that SetPaper/SetPaperSizeUser
2371
    // may have changed — orientation should follow the document.
2372
0
    if (mxOrientationBox->get_active() == ORIENTATION_AUTOMATIC
2373
0
        && aPrt->GetOrientation() != eOrientBefore)
2374
0
    {
2375
0
        aPrt->SetOrientation(eOrientBefore);
2376
0
    }
2377
2378
0
    updatePageSize(mxOrientationBox->get_active());
2379
2380
0
    schedulePreviewUpdate(false);
2381
0
}
2382
2383
IMPL_LINK_NOARG(PrintDialog, MetricSpinModifyHdl, weld::MetricSpinButton&, void)
2384
0
{
2385
0
    checkControlDependencies();
2386
0
    updateNupFromPages();
2387
0
}
2388
2389
IMPL_LINK_NOARG(PrintDialog, FocusOutHdl, weld::Widget&, void)
2390
0
{
2391
0
    ActivateHdl(*mxPageEdit);
2392
0
}
2393
2394
IMPL_LINK_NOARG(PrintDialog, ActivateHdl, weld::Entry&, bool)
2395
0
{
2396
0
    sal_Int32 nPage = mxPageEdit->get_text().toInt32();
2397
0
    if (nPage < 1)
2398
0
    {
2399
0
        nPage = 1;
2400
0
        mxPageEdit->set_text(u"1"_ustr);
2401
0
    }
2402
0
    else if (nPage > mnCachedPages)
2403
0
    {
2404
0
        nPage = mnCachedPages;
2405
0
        mxPageEdit->set_text(OUString::number(mnCachedPages));
2406
0
    }
2407
0
    int nNewCurPage = nPage - 1;
2408
0
    if (nNewCurPage != mnCurPage)
2409
0
    {
2410
0
        mnCurPage = nNewCurPage;
2411
0
        schedulePreviewUpdate(true);
2412
0
    }
2413
0
    return true;
2414
0
}
2415
2416
IMPL_LINK( PrintDialog, SpinModifyHdl, weld::SpinButton&, rEdit, void )
2417
0
{
2418
0
    checkControlDependencies();
2419
0
    if (&rEdit == mxNupRowsEdt.get() || &rEdit == mxNupColEdt.get())
2420
0
    {
2421
0
       updateNupFromPages();
2422
0
    }
2423
0
    else if( &rEdit == mxCopyCountField.get() )
2424
0
    {
2425
0
        maPController->setValue( u"CopyCount"_ustr,
2426
0
                               Any( sal_Int32(mxCopyCountField->get_value()) ) );
2427
0
        maPController->setValue( u"Collate"_ustr,
2428
0
                               Any( isCollate() ) );
2429
0
    }
2430
0
}
2431
2432
IMPL_LINK( PrintDialog, UIOption_CheckHdl, weld::Toggleable&, i_rBox, void )
2433
0
{
2434
0
    PropertyValue* pVal = getValueForWindow( &i_rBox );
2435
0
    if( pVal )
2436
0
    {
2437
0
        makeEnabled( &i_rBox );
2438
2439
0
        bool bVal = i_rBox.get_active();
2440
0
        pVal->Value <<= bVal;
2441
2442
0
        checkOptionalControlDependencies();
2443
2444
        // update preview and page settings
2445
0
        schedulePreviewUpdate(false);
2446
0
    }
2447
0
}
2448
2449
IMPL_LINK( PrintDialog, UIOption_RadioHdl, weld::Toggleable&, i_rBtn, void )
2450
0
{
2451
    // this handler gets called for all radiobuttons that get unchecked, too
2452
    // however we only want one notification for the new value (that is for
2453
    // the button that gets checked)
2454
0
    if( !i_rBtn.get_active() )
2455
0
        return;
2456
2457
0
    PropertyValue* pVal = getValueForWindow( &i_rBtn );
2458
0
    auto it = maControlToNumValMap.find( &i_rBtn );
2459
0
    if( !(pVal && it != maControlToNumValMap.end()) )
2460
0
        return;
2461
2462
0
    makeEnabled( &i_rBtn );
2463
2464
0
    sal_Int32 nVal = it->second;
2465
0
    pVal->Value <<= nVal;
2466
2467
    // Writer-specific handling
2468
0
    if (pVal->Name == "PrintContent")
2469
0
    {
2470
0
        PropertyValue* pPageRange = maPController->getValue(u"PageRange"_ustr);
2471
0
        if (pPageRange)
2472
0
        {
2473
0
            if (nVal == 1)
2474
0
                pPageRange->Value <<= mxPageRangeEdit->get_text();
2475
0
            else if (nVal == 3)
2476
0
                pPageRange->Value <<= OUString::number(mnInitialPage);
2477
0
        }
2478
0
    }
2479
2480
0
    updateOrientationBox();
2481
2482
0
    checkOptionalControlDependencies();
2483
2484
    // tdf#41205 give focus to the page range edit if the corresponding radio button was selected
2485
0
    if (pVal->Name == "PrintContent" && mxPageRangesRadioButton->get_active())
2486
0
        mxPageRangeEdit->grab_focus();
2487
2488
    // update preview and page settings
2489
0
    schedulePreviewUpdate(false);
2490
0
}
2491
2492
IMPL_LINK( PrintDialog, UIOption_SelectHdl, weld::ComboBox&, i_rBox, void )
2493
0
{
2494
0
    PropertyValue* pVal = getValueForWindow( &i_rBox );
2495
0
    if( !pVal )
2496
0
        return;
2497
2498
0
    makeEnabled( &i_rBox );
2499
2500
0
    sal_Int32 nVal( i_rBox.get_active() );
2501
0
    pVal->Value <<= nVal;
2502
2503
    //If we are in impress we start in print slides mode and get a
2504
    //maFirstPageSize for slides which are usually landscape mode, if we
2505
    //change to notes which are usually in portrait mode, and then visit
2506
    //n-up print, we will assume notes are in landscape unless we throw
2507
    //away maFirstPageSize when we change page content type
2508
0
    if (pVal->Name == "PageContentType")
2509
0
    {
2510
0
        maFirstPageSize = Size();
2511
2512
0
        css::uno::Sequence<sal_Bool> aChoicesDisabled{
2513
0
            false, // Original size
2514
0
            false, // Fit to printable page
2515
0
            (nVal == 2) /*Notes*/ ? true : false, // disable/enable Multiple sheets of paper
2516
0
            (nVal == 2) /*Notes*/ ? true : false  // disable/enable Tile sheet of paper
2517
0
        };
2518
0
        maPController->setUIChoicesDisabled(u"PageOptions"_ustr, aChoicesDisabled);
2519
0
    }
2520
2521
0
    checkOptionalControlDependencies();
2522
2523
0
    updatePageSize(mxOrientationBox->get_active());
2524
2525
    // update preview and page settings
2526
0
    schedulePreviewUpdate(false);
2527
0
}
2528
2529
IMPL_LINK( PrintDialog, UIOption_SpinModifyHdl, weld::SpinButton&, i_rBox, void )
2530
0
{
2531
0
    PropertyValue* pVal = getValueForWindow( &i_rBox );
2532
0
    if( pVal )
2533
0
    {
2534
0
        makeEnabled( &i_rBox );
2535
2536
0
        sal_Int64 nVal = i_rBox.get_value();
2537
0
        pVal->Value <<= nVal;
2538
2539
0
        checkOptionalControlDependencies();
2540
2541
        // update preview and page settings
2542
0
        schedulePreviewUpdate(false);
2543
0
    }
2544
0
}
2545
2546
IMPL_LINK(PrintDialog, UIOption_EntryModifyHdl, weld::TextWidget&, i_rBox, void)
2547
0
{
2548
0
    PropertyValue* pVal = getValueForWindow( &i_rBox );
2549
0
    if( pVal && mxPageRangesRadioButton->get_active() )
2550
0
    {
2551
0
        makeEnabled( &i_rBox );
2552
2553
0
        OUString aVal( i_rBox.get_text() );
2554
0
        pVal->Value <<= aVal;
2555
2556
0
        checkOptionalControlDependencies();
2557
2558
0
        if (pVal->Name == "PageRange")
2559
0
            maPageRangeUpdateTimer.Start();
2560
0
        else
2561
0
            schedulePreviewUpdate(false);
2562
2563
0
    }
2564
0
}
2565
2566
void PrintDialog::previewForward()
2567
0
{
2568
0
    sal_Int32 nValue = mxPageEdit->get_text().toInt32() + 1;
2569
0
    if (nValue <= mnCachedPages)
2570
0
    {
2571
0
        mxPageEdit->set_text(OUString::number(nValue));
2572
0
        ActivateHdl(*mxPageEdit);
2573
0
    }
2574
0
}
2575
2576
void PrintDialog::previewBackward()
2577
0
{
2578
0
    sal_Int32 nValue = mxPageEdit->get_text().toInt32() - 1;
2579
0
    if (nValue >= 1)
2580
0
    {
2581
0
        mxPageEdit->set_text(OUString::number(nValue));
2582
0
        ActivateHdl(*mxPageEdit);
2583
0
    }
2584
0
}
2585
2586
void PrintDialog::previewFirst()
2587
0
{
2588
0
    mxPageEdit->set_text(u"1"_ustr);
2589
0
    ActivateHdl(*mxPageEdit);
2590
0
}
2591
2592
void PrintDialog::previewLast()
2593
0
{
2594
0
    mxPageEdit->set_text(OUString::number(mnCachedPages));
2595
0
    ActivateHdl(*mxPageEdit);
2596
0
}
2597
2598
2599
static OUString getNewLabel(const OUString& aLabel, int i_nCurr, int i_nMax)
2600
0
{
2601
0
    OUString aNewText( aLabel.replaceFirst( "%p", OUString::number( i_nCurr ) ) );
2602
0
    aNewText = aNewText.replaceFirst( "%n", OUString::number( i_nMax ) );
2603
2604
0
    return aNewText;
2605
0
}
2606
2607
// PrintProgressDialog
2608
PrintProgressDialog::PrintProgressDialog(weld::Window* i_pParent, int i_nMax)
2609
0
    : GenericDialogController(i_pParent, u"vcl/ui/printprogressdialog.ui"_ustr, u"PrintProgressDialog"_ustr)
2610
0
    , mbCanceled(false)
2611
0
    , mnCur(0)
2612
0
    , mnMax(i_nMax)
2613
0
    , mxText(m_xBuilder->weld_label(u"label"_ustr))
2614
0
    , mxProgress(m_xBuilder->weld_progress_bar(u"progressbar"_ustr))
2615
0
    , mxButton(m_xBuilder->weld_button(u"cancel"_ustr))
2616
0
{
2617
0
    if( mnMax < 1 )
2618
0
        mnMax = 1;
2619
2620
0
    maStr = mxText->get_label();
2621
2622
    //just multiply largest value by 10 and take the width of that string as
2623
    //the max size we will want
2624
0
    mxText->set_label(getNewLabel(maStr, mnMax * 10, mnMax * 10));
2625
0
    mxText->set_size_request(mxText->get_preferred_size().Width(), -1);
2626
2627
    //Pick a useful max width
2628
0
    mxProgress->set_size_request(mxProgress->get_approximate_digit_width() * 25, -1);
2629
2630
0
    mxButton->connect_clicked( LINK( this, PrintProgressDialog, ClickHdl ) );
2631
2632
    // after this patch f7157f04fab298423e2c4f6a7e5f8e361164b15f, we have seen the calc Max string (sometimes) look above
2633
    // now init to the right start values
2634
0
    mxText->set_label(getNewLabel(maStr, mnCur, mnMax));
2635
0
}
2636
2637
PrintProgressDialog::~PrintProgressDialog()
2638
0
{
2639
0
}
2640
2641
IMPL_LINK_NOARG(PrintProgressDialog, ClickHdl, weld::Button&, void)
2642
0
{
2643
0
    mbCanceled = true;
2644
0
}
2645
2646
void PrintProgressDialog::setProgress( int i_nCurrent )
2647
0
{
2648
0
    mnCur = i_nCurrent;
2649
2650
0
    if( mnMax < 1 )
2651
0
        mnMax = 1;
2652
2653
0
    mxText->set_label(getNewLabel(maStr, mnCur, mnMax));
2654
2655
    // here view the dialog, with the right label
2656
0
    mxProgress->set_percentage(mnCur*100/mnMax);
2657
0
}
2658
2659
void PrintProgressDialog::tick()
2660
0
{
2661
0
    if( mnCur < mnMax )
2662
0
        setProgress( ++mnCur );
2663
0
}
2664
2665
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */