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