1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
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>
42 #include <bitmaps.hlst>
43 #include <configsettings.hxx>
44 #include <printdlg.hxx>
45 #include <strings.hrc>
48 #include <com/sun/star/beans/PropertyValue.hpp>
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
;
58 ORIENTATION_AUTOMATIC
,
64 bool lcl_ListBoxCompare( const OUString
& rStr1
, const OUString
& rStr2
)
66 return vcl::NaturalSortCompare( rStr1
, rStr2
) < 0;
70 PrintDialog::PrintPreviewWindow::PrintPreviewWindow(PrintDialog
* pDialog
)
72 , maOrigSize( 10, 10 )
73 , mnDPIX(Application::GetDefaultDevice()->GetDPIX())
74 , mnDPIY(Application::GetDefaultDevice()->GetDPIY())
75 , mbGreyscale( false )
79 PrintDialog::PrintPreviewWindow::~PrintPreviewWindow()
83 void PrintDialog::PrintPreviewWindow::Resize()
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) );
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() )
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());
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());
111 aScaledSize
.setWidth( tools::Long(aScaledSize
.Width()*fScale
) );
112 aScaledSize
.setHeight( tools::Long(aScaledSize
.Height()*fScale
) );
114 maPreviewSize
= aScaledSize
;
116 // check and evtl. recreate preview bitmap
117 preparePreviewBitmap();
120 void PrintDialog::PrintPreviewWindow::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
&)
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();
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);
135 auto nWidth
= rRenderContext
.GetTextWidth(maHorzText
);
137 auto nStart
= aOffset
.X() + (maPreviewSize
.Width() - nWidth
) / 2;
138 rRenderContext
.DrawText(Point(nStart
, aOffset
.Y() - nTextHeight
), maHorzText
, 0, maHorzText
.getLength());
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);
148 rRenderContext
.Push(PushFlags::FONT
);
149 vcl::Font
aFont(rRenderContext
.GetFont());
150 aFont
.SetOrientation(900_deg10
);
151 rRenderContext
.SetFont(aFont
);
153 auto nLeft
= aOffset
.X() - nTextHeight
;
155 auto nWidth
= rRenderContext
.GetTextWidth(maVertText
);
156 auto nStart
= aOffset
.Y() + (maPreviewSize
.Height() + nWidth
) / 2;
158 rRenderContext
.DrawText(Point(nLeft
, nStart
), maVertText
, 0, maVertText
.getLength());
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);
165 rRenderContext
.Pop();
168 if (!maReplacementString
.isEmpty())
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
);
178 BitmapEx
aPreviewBitmap(maPreviewBitmap
);
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
);
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
);
192 rRenderContext
.Pop();
195 bool PrintDialog::PrintPreviewWindow::Command( const CommandEvent
& rEvt
)
197 if( rEvt
.GetCommand() == CommandEventId::Wheel
)
199 const CommandWheelData
* pWheelData
= rEvt
.GetWheelData();
200 if(pWheelData
->GetDelta() > 0)
201 mpDialog
->previewForward();
202 else if (pWheelData
->GetDelta() < 0)
203 mpDialog
->previewBackward();
206 return CustomWidgetController::Command(rEvt
);
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
,
218 maMtf
= i_rNewPreview
;
221 maOrigSize
= i_rOrigSize
;
222 maReplacementString
= i_rReplacement
;
223 mbGreyscale
= i_bGreyscale
;
225 // use correct measurements
226 const LocaleDataWrapper
& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
227 o3tl::Length eUnit
= o3tl::Length::mm
;
229 if( rLocWrap
.getMeasurementSystemEnum() == MeasurementSystem::US
)
231 eUnit
= o3tl::Length::in100
;
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() )
240 aBuf
.append( OUString::Concat(" (") + i_rPaperName
+ ")" );
242 maHorzText
= aBuf
.makeStringAndClear();
244 aNumText
= rLocWrap
.getNum( aLogicPaperSize
.Height(), nDigits
);
245 aBuf
.append( aNumText
+ " " );
246 aBuf
.appendAscii( eUnit
== o3tl::Length::mm
? "mm" : "in" );
247 maVertText
= aBuf
.makeStringAndClear();
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();
253 // sets/calculates e.g. maPreviewSize
254 // also triggers preparePreviewBitmap()
260 void PrintDialog::PrintPreviewWindow::preparePreviewBitmap()
262 if(maPreviewSize
.IsEmpty())
264 // not yet fully initialized, no need to prepare anything
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);
273 // check how big (squarePixels) the preview is currently (with
274 // max value of MaxSquarePixels)
275 const sal_uInt32
nCurrentSquarePixels(
278 static_cast<sal_uInt32
>(maPreviewBitmap
.GetSizePixel().getWidth())
279 * static_cast<sal_uInt32
>(maPreviewBitmap
.GetSizePixel().getHeight())));
281 // check how big (squarePixels) the preview needs to be (with
282 // max value of MaxSquarePixels)
283 const sal_uInt32
nRequiredSquarePixels(
286 static_cast<sal_uInt32
>(maPreviewSize
.getWidth())
287 * static_cast<sal_uInt32
>(maPreviewSize
.getHeight())));
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
))
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.
301 // already sufficient, done.
305 // check if we have enough square pixels e.g for 8x8 pixels
306 if(nRequiredSquarePixels
< 64)
308 // too small preview - let it empty
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(
318 static_cast<sal_uInt32
>(maPreviewSize
.getWidth() * fExtraSpaceFactor
)
319 * static_cast<sal_uInt32
>(maPreviewSize
.getHeight() * fExtraSpaceFactor
)));
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
));
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()));
336 // test as equal up to 0.1% (0.001)
337 if(fPreviewSizeSquare
!= 0.0 && fabs((fScaledSizeSquare
/ fPreviewSizeSquare
) - 1.0) < 0.001)
339 // maximum is reached, avoid bigger scaling
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
);
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());
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);
382 if(fScale
< fMinimumScale
)
384 fFactor
= fMinimumScale
/ fScale
;
385 fScale
= fMinimumScale
;
387 double fWidth(aScaledSize
.getWidth() * fFactor
);
388 double fHeight(aScaledSize
.getHeight() * fFactor
);
389 const double fNewNeededPixels(fWidth
* fHeight
);
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);
398 if(fNewNeededPixels
> fMaximumQualitySquare
)
400 const double fCorrection(fMaximumQualitySquare
/fNewNeededPixels
);
401 fWidth
*= fCorrection
;
402 fHeight
*= fCorrection
;
403 fScale
*= fCorrection
;
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
) );
411 pPrerenderVDev
->EnableOutput();
412 pPrerenderVDev
->SetBackground( Wallpaper(COL_WHITE
) );
413 pPrerenderVDev
->Erase();
414 pPrerenderVDev
->SetMapMode(MapMode(MapUnit::Map100thMM
));
416 pPrerenderVDev
->SetDrawMode( pPrerenderVDev
->GetDrawMode() |
417 ( DrawModeFlags::GrayLine
| DrawModeFlags::GrayFill
| DrawModeFlags::GrayText
|
418 DrawModeFlags::GrayBitmap
| DrawModeFlags::GrayGradient
) );
420 // Copy, Scale and Paint Metafile
421 GDIMetaFile
aMtf( maMtf
);
423 aMtf
.Scale( fScale
, fScale
);
425 aMtf
.Play(*pPrerenderVDev
, Point(0, 0), aLogicSize
);
427 pPrerenderVDev
->SetMapMode(MapMode(MapUnit::MapPixel
));
428 maPreviewBitmap
= pPrerenderVDev
->GetBitmapEx(Point(0, 0), pPrerenderVDev
->GetOutputSizePixel());
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
);
439 PrintDialog::ShowNupOrderWindow::ShowNupOrderWindow()
440 : mnOrderMode( NupOrderType::LRTB
)
446 void PrintDialog::ShowNupOrderWindow::SetDrawingArea(weld::DrawingArea
* pDrawingArea
)
449 pDrawingArea
->set_size_request(aSize
.Width(), aSize
.Height());
450 CustomWidgetController::SetDrawingArea(pDrawingArea
);
451 SetOutputSizePixel(aSize
);
454 void PrintDialog::ShowNupOrderWindow::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*i_rRect*/)
456 rRenderContext
.SetMapMode(MapMode(MapUnit::MapPixel
));
457 rRenderContext
.SetTextColor(rRenderContext
.GetSettings().GetStyleSettings().GetFieldTextColor());
458 rRenderContext
.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFieldColor()));
459 rRenderContext
.Erase();
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;
475 aFont
.SetFontSize(Size( 0, nFontHeight
));
476 rRenderContext
.SetFont(aFont
);
477 tools::Long nTextHeight
= rRenderContext
.GetTextHeight();
478 for (int i
= 0; i
< nPages
; i
++)
480 OUString
aPageText(OUString::number(i
+ 1));
484 case NupOrderType::LRTB
:
485 nX
= (i
% mnColumns
);
486 nY
= (i
/ mnColumns
);
488 case NupOrderType::TBLR
:
492 case NupOrderType::RLTB
:
493 nX
= mnColumns
- 1 - (i
% mnColumns
);
494 nY
= (i
/ mnColumns
);
496 case NupOrderType::TBRL
:
497 nX
= mnColumns
- 1 - (i
/ mnRows
);
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
);
507 DecorationView
aDecorationView(&rRenderContext
);
508 aDecorationView
.DrawFrame(tools::Rectangle(Point(0, 0), aOutSize
), DrawFrameStyle::Group
);
511 Size
const & PrintDialog::getJobPageSize()
513 if( maFirstPageSize
.IsEmpty() )
515 maFirstPageSize
= maNupPortraitSize
;
517 if( maPController
->getPageCountProtected() > 0 )
519 PrinterController::PageSize aPageSize
= maPController
->getPageFile( 0, aMtf
, true );
520 maFirstPageSize
= aPageSize
.aSize
;
523 return maFirstPageSize
;
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
) )
584 , mbCollateAlwaysOff(false)
585 , mbShowLayoutFrame( true )
586 , maUpdatePreviewIdle("Print Dialog Update Preview Idle")
587 , maUpdatePreviewNoCacheIdle("Print Dialog Update Preview (no cache) Idle")
589 // save printbutton text, gets exchanged occasionally with print to file
590 maPrintText
= mxOKButton
->get_label();
592 maPageStr
= mxNumPagesText
->get_label();
594 mxPrinters
->append_text(maPrintToFileText
);
595 // fill printer listbox
596 std::vector
< OUString
> rQueues( Printer::GetPrinterQueues() );
597 std::sort( rQueues
.begin(), rQueues
.end(), lcl_ListBoxCompare
);
598 for( const auto& rQueue
: rQueues
)
600 mxPrinters
->append_text(rQueue
);
602 // select current printer
603 if (mxPrinters
->find_text(maPController
->getPrinter()->GetName()) != -1)
604 mxPrinters
->set_active_text(maPController
->getPrinter()->GetName());
607 // fall back to last printer
608 SettingsConfigItem
* pItem
= SettingsConfigItem::get();
609 OUString
aValue( pItem
->getValue( u
"PrintDialog"_ustr
,
610 u
"LastPrinter"_ustr
) );
611 if (mxPrinters
->find_text(aValue
) != -1)
613 mxPrinters
->set_active_text(aValue
);
614 maPController
->setPrinter( VclPtrInstance
<Printer
>( aValue
) );
618 // fall back to default printer
619 mxPrinters
->set_active_text(Printer::GetDefaultPrinterName());
620 maPController
->setPrinter( VclPtrInstance
<Printer
>( Printer::GetDefaultPrinterName() ) );
624 // not printing to file
625 maPController
->resetPrinterOptions( false );
627 // update the text fields for the printer
630 // setup dependencies
631 checkControlDependencies();
633 // setup paper sides box
634 setupPaperSidesBox();
636 // set initial focus to "Printer"
637 mxPrinters
->grab_focus();
639 // setup sizes for N-Up
640 Size
aNupSize( maPController
->getPrinter()->PixelToLogic(
641 maPController
->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM
) ) );
642 if( maPController
->getPrinter()->GetOrientation() == Orientation::Landscape
)
644 maNupLandscapeSize
= aNupSize
;
645 // coverity[swapped_arguments : FALSE] - this is in the correct order
646 maNupPortraitSize
= Size( aNupSize
.Height(), aNupSize
.Width() );
650 maNupPortraitSize
= aNupSize
;
651 // coverity[swapped_arguments : FALSE] - this is in the correct order
652 maNupLandscapeSize
= Size( aNupSize
.Height(), aNupSize
.Width() );
655 maUpdatePreviewIdle
.SetPriority(TaskPriority::POST_PAINT
);
656 maUpdatePreviewIdle
.SetInvokeHandler(LINK( this, PrintDialog
, updatePreviewIdle
));
657 maUpdatePreviewNoCacheIdle
.SetPriority(TaskPriority::POST_PAINT
);
658 maUpdatePreviewNoCacheIdle
.SetInvokeHandler(LINK(this, PrintDialog
, updatePreviewNoCacheIdle
));
660 initFromMultiPageSetup( maPController
->getMultipage() );
662 // setup optional UI options set by application
665 // hide layout frame if unwanted
666 mxPageLayoutFrame
->set_visible(mbShowLayoutFrame
);
668 // restore settings from last run
672 mxOKButton
->connect_clicked(LINK(this, PrintDialog
, ClickHdl
));
673 mxCancelButton
->connect_clicked(LINK(this, PrintDialog
, ClickHdl
));
674 mxSetupButton
->connect_clicked( LINK( this, PrintDialog
, ClickHdl
) );
675 mxBackwardBtn
->connect_clicked(LINK(this, PrintDialog
, ClickHdl
));
676 mxForwardBtn
->connect_clicked(LINK(this, PrintDialog
, ClickHdl
));
677 mxFirstBtn
->connect_clicked(LINK(this, PrintDialog
, ClickHdl
));
678 mxLastBtn
->connect_clicked( LINK( this, PrintDialog
, ClickHdl
) );
681 mxReverseOrderBox
->connect_toggled( LINK( this, PrintDialog
, ToggleHdl
) );
682 mxCollateBox
->connect_toggled( LINK( this, PrintDialog
, ToggleHdl
) );
683 mxSingleJobsBox
->connect_toggled( LINK( this, PrintDialog
, ToggleHdl
) );
684 mxBrochureBtn
->connect_toggled( LINK( this, PrintDialog
, ToggleHdl
) );
685 mxPreviewBox
->connect_toggled( LINK( this, PrintDialog
, ToggleHdl
) );
686 mxBorderCB
->connect_toggled( LINK( this, PrintDialog
, ToggleHdl
) );
689 mxPrinters
->connect_changed( LINK( this, PrintDialog
, SelectHdl
) );
690 mxPaperSidesBox
->connect_changed( LINK( this, PrintDialog
, SelectHdl
) );
691 mxNupPagesBox
->connect_changed( LINK( this, PrintDialog
, SelectHdl
) );
692 mxOrientationBox
->connect_changed( LINK( this, PrintDialog
, SelectHdl
) );
693 mxNupOrderBox
->connect_changed( LINK( this, PrintDialog
, SelectHdl
) );
694 mxPaperSizeBox
->connect_changed( LINK( this, PrintDialog
, SelectHdl
) );
697 mxPageEdit
->connect_activate( LINK( this, PrintDialog
, ActivateHdl
) );
698 mxPageEdit
->connect_focus_out( LINK( this, PrintDialog
, FocusOutHdl
) );
699 mxCopyCountField
->connect_value_changed( LINK( this, PrintDialog
, SpinModifyHdl
) );
700 mxNupColEdt
->connect_value_changed( LINK( this, PrintDialog
, SpinModifyHdl
) );
701 mxNupRowsEdt
->connect_value_changed( LINK( this, PrintDialog
, SpinModifyHdl
) );
702 mxPageMarginEdt
->connect_value_changed( LINK( this, PrintDialog
, MetricSpinModifyHdl
) );
703 mxSheetMarginEdt
->connect_value_changed( LINK( this, PrintDialog
, MetricSpinModifyHdl
) );
705 updateNupFromPages();
707 // tdf#129180 Delay setting the default value in the Paper Size list
708 // set paper sizes listbox
711 mxRangeExpander
->set_expanded(
712 officecfg::Office::Common::Print::Dialog::RangeSectionExpanded::get());
713 mxLayoutExpander
->set_expanded(
714 officecfg::Office::Common::Print::Dialog::LayoutSectionExpanded::get());
716 // lock the dialog height, regardless of later expander state
717 mxScrolledWindow
->set_size_request(
718 mxScrolledWindow
->get_preferred_size().Width() + mxScrolledWindow
->get_scroll_thickness(),
721 m_xDialog
->set_centered_on_parent(true);
724 PrintDialog::~PrintDialog()
726 std::shared_ptr
<comphelper::ConfigurationChanges
> batch(comphelper::ConfigurationChanges::create());
727 officecfg::Office::Common::Print::Dialog::RangeSectionExpanded::set(mxRangeExpander
->get_expanded(), batch
);
728 officecfg::Office::Common::Print::Dialog::LayoutSectionExpanded::set(mxLayoutExpander
->get_expanded(), batch
);
732 void PrintDialog::setupPaperSidesBox()
734 DuplexMode eDuplex
= maPController
->getPrinter()->GetDuplexMode();
736 if ( eDuplex
== DuplexMode::Unknown
|| isPrintToFile() )
738 mxPaperSidesBox
->set_active( 0 );
739 mxPaperSidesBox
->set_sensitive( false );
743 mxPaperSidesBox
->set_active( static_cast<sal_Int32
>(eDuplex
) - 1 );
744 mxPaperSidesBox
->set_sensitive( true );
748 void PrintDialog::storeToSettings()
750 SettingsConfigItem
* pItem
= SettingsConfigItem::get();
752 pItem
->setValue( u
"PrintDialog"_ustr
,
754 isPrintToFile() ? Printer::GetDefaultPrinterName()
755 : mxPrinters
->get_active_text() );
757 pItem
->setValue( u
"PrintDialog"_ustr
,
759 mxTabCtrl
->get_tab_label_text(mxTabCtrl
->get_current_page_ident()));
761 pItem
->setValue( u
"PrintDialog"_ustr
,
763 m_xDialog
->get_window_state(vcl::WindowDataMask::All
) );
765 pItem
->setValue( u
"PrintDialog"_ustr
,
767 mxCopyCountField
->get_text() );
769 pItem
->setValue( u
"PrintDialog"_ustr
,
771 mxCollateBox
->get_active() ? u
"true"_ustr
:
774 pItem
->setValue( u
"PrintDialog"_ustr
,
775 u
"CollateSingleJobs"_ustr
,
776 mxSingleJobsBox
->get_active() ? u
"true"_ustr
:
779 pItem
->setValue( u
"PrintDialog"_ustr
,
781 hasPreview() ? u
"true"_ustr
:
787 void PrintDialog::readFromSettings()
789 SettingsConfigItem
* pItem
= SettingsConfigItem::get();
791 // read last selected tab page; if it exists, activate it
792 OUString aValue
= pItem
->getValue( u
"PrintDialog"_ustr
,
794 sal_uInt16 nCount
= mxTabCtrl
->get_n_pages();
795 for (sal_uInt16 i
= 0; i
< nCount
; ++i
)
797 OUString sPageId
= mxTabCtrl
->get_page_ident(i
);
798 if (aValue
== mxTabCtrl
->get_tab_label_text(sPageId
))
800 mxTabCtrl
->set_current_page(sPageId
);
805 // persistent window state
806 aValue
= pItem
->getValue( u
"PrintDialog"_ustr
,
807 u
"WindowState"_ustr
);
808 if (!aValue
.isEmpty())
809 m_xDialog
->set_window_state(aValue
);
812 aValue
= pItem
->getValue( u
"PrintDialog"_ustr
,
813 u
"CollateBox"_ustr
);
814 if( aValue
.equalsIgnoreAsciiCase("alwaysoff") )
816 mbCollateAlwaysOff
= true;
817 mxCollateBox
->set_active( false );
818 mxCollateBox
->set_sensitive( false );
822 mbCollateAlwaysOff
= false;
823 aValue
= pItem
->getValue( u
"PrintDialog"_ustr
,
825 mxCollateBox
->set_active( aValue
.equalsIgnoreAsciiCase("true") );
828 // collate single jobs
829 aValue
= pItem
->getValue( u
"PrintDialog"_ustr
,
830 u
"CollateSingleJobs"_ustr
);
831 mxSingleJobsBox
->set_active(aValue
.equalsIgnoreAsciiCase("true"));
834 aValue
= pItem
->getValue( u
"PrintDialog"_ustr
,
835 u
"HasPreview"_ustr
);
836 if ( aValue
.equalsIgnoreAsciiCase("false") )
837 mxPreviewBox
->set_active( false );
839 mxPreviewBox
->set_active( true );
843 void PrintDialog::setPaperSizes()
845 mxPaperSizeBox
->clear();
846 mxPaperSizeBox
->set_active(-1);
848 VclPtr
<Printer
> aPrt( maPController
->getPrinter() );
849 mePaper
= aPrt
->GetPaper();
851 // Related: tdf#159995 allow setting the page size when printing to file
852 // On macOS, printing to file just saves to PDF using a native print
853 // job just like the native print dialog's PDF > Save as PDF... option.
854 // Also, as noted in tdf#163558, the native print dialog could support
855 // changing the page size eventually (and already does in Apple's
856 // Safari and TextEdit applications) so allow changing of the page
857 // size in the non-native print dialog on macOS as well.
859 if ( isPrintToFile() )
861 mxPaperSizeBox
->set_sensitive( false );
866 int nExactMatch
= -1;
868 int nRotatedSizeMatch
= -1;
869 Size aSizeOfPaper
= aPrt
->GetSizeOfPaper();
870 PaperInfo
aPaperInfo(aSizeOfPaper
.getWidth(), aSizeOfPaper
.getHeight());
871 // coverity[swapped_arguments : FALSE] - this is in the correct order
872 PaperInfo
aRotatedPaperInfo(aSizeOfPaper
.getHeight(), aSizeOfPaper
.getWidth());
873 const LocaleDataWrapper
& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
874 o3tl::Length eUnit
= o3tl::Length::mm
;
876 if( rLocWrap
.getMeasurementSystemEnum() == MeasurementSystem::US
)
878 eUnit
= o3tl::Length::in100
;
881 for (int nPaper
= 0; nPaper
< aPrt
->GetPaperInfoCount(); nPaper
++)
883 PaperInfo aInfo
= aPrt
->GetPaperInfo( nPaper
);
884 aInfo
.doSloppyFit(true);
885 Paper ePaper
= aInfo
.getPaper();
887 Size aSize
= aPrt
->GetPaperSize( nPaper
);
888 Size
aLogicPaperSize( o3tl::convert(aSize
, o3tl::Length::mm100
, eUnit
) );
890 OUString
aWidth( rLocWrap
.getNum( aLogicPaperSize
.Width(), nDigits
) );
891 OUString
aHeight( rLocWrap
.getNum( aLogicPaperSize
.Height(), nDigits
) );
892 OUString aUnit
= eUnit
== o3tl::Length::mm
? u
"mm"_ustr
: u
"in"_ustr
;
895 // Paper sizes that we don't know of but the system printer driver lists are not "User
896 // Defined". Displaying them as such is just confusing.
897 if (ePaper
!= PAPER_USER
)
898 aPaperName
= Printer::GetPaperName( ePaper
) + " ";
900 aPaperName
+= aWidth
+ aUnit
+ " x " + aHeight
+ aUnit
;
902 mxPaperSizeBox
->append_text(aPaperName
);
904 if (ePaper
!= PAPER_USER
&& ePaper
== mePaper
)
905 nExactMatch
= nPaper
;
907 if (ePaper
== PAPER_USER
&& aInfo
.sloppyEqual(aPaperInfo
))
910 if (ePaper
== PAPER_USER
&& aInfo
.sloppyEqual(aRotatedPaperInfo
))
911 nRotatedSizeMatch
= nPaper
;
914 if (nExactMatch
!= -1)
915 mxPaperSizeBox
->set_active(nExactMatch
);
916 else if (nSizeMatch
!= -1)
917 mxPaperSizeBox
->set_active(nSizeMatch
);
918 else if (nRotatedSizeMatch
!= -1)
919 mxPaperSizeBox
->set_active(nRotatedSizeMatch
);
921 mxPaperSizeBox
->set_sensitive( true );
925 void PrintDialog::updatePrinterText()
927 const OUString
aDefPrt( Printer::GetDefaultPrinterName() );
928 const QueueInfo
* pInfo
= Printer::GetQueueInfo( mxPrinters
->get_active_text(), true );
931 // FIXME: status text
933 if( aDefPrt
== pInfo
->GetPrinterName() )
934 aStatus
= maDefPrtText
;
935 mxStatusTxt
->set_label( aStatus
);
939 mxStatusTxt
->set_label( OUString() );
943 void PrintDialog::setPreviewText()
945 OUString
aNewText( maPageStr
.replaceFirst( "%n", OUString::number( mnCachedPages
) ) );
946 mxNumPagesText
->set_label( aNewText
);
949 IMPL_LINK_NOARG(PrintDialog
, updatePreviewIdle
, Timer
*, void)
951 preparePreview(true);
954 IMPL_LINK_NOARG(PrintDialog
, updatePreviewNoCacheIdle
, Timer
*, void)
956 preparePreview(false);
959 void PrintDialog::preparePreview( bool i_bMayUseCache
)
961 VclPtr
<Printer
> aPrt( maPController
->getPrinter() );
962 Size aCurPageSize
= aPrt
->PixelToLogic( aPrt
->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM
) );
963 // tdf#123076 Get paper size for the preview top label
964 mePaper
= aPrt
->GetPaper();
967 // page range may have changed depending on options
968 sal_Int32 nPages
= maPController
->getFilteredPageCount();
969 mnCachedPages
= nPages
;
972 updatePageRange(nPages
);
978 mxPreview
->setPreview( aMtf
, aCurPageSize
,
979 Printer::GetPaperName( mePaper
),
981 aPrt
->GetDPIX(), aPrt
->GetDPIY(),
982 aPrt
->GetPrinterOptions().IsConvertToGreyscales()
985 mxForwardBtn
->set_sensitive( false );
986 mxBackwardBtn
->set_sensitive( false );
987 mxFirstBtn
->set_sensitive( false );
988 mxLastBtn
->set_sensitive( false );
990 mxPageEdit
->set_sensitive( false );
995 if( mnCurPage
>= nPages
)
996 mnCurPage
= nPages
-1;
999 mxPageEdit
->set_text(OUString::number(mnCurPage
+ 1));
1003 PrinterController::PageSize aPageSize
=
1004 maPController
->getFilteredPageFile( mnCurPage
, aMtf
, i_bMayUseCache
);
1005 aCurPageSize
= aPrt
->PixelToLogic(aPrt
->GetPaperSizePixel(), MapMode(MapUnit::Map100thMM
));
1006 if( ! aPageSize
.bFullPaper
)
1008 const MapMode
aMapMode( MapUnit::Map100thMM
);
1009 Point
aOff( aPrt
->PixelToLogic( aPrt
->GetPageOffsetPixel(), aMapMode
) );
1010 aMtf
.Move( aOff
.X(), aOff
.Y() );
1012 // tdf#150561: page size may have changed so sync mePaper with it
1013 mePaper
= aPrt
->GetPaper();
1016 mxPreview
->setPreview( aMtf
, aCurPageSize
,
1017 Printer::GetPaperName( mePaper
),
1018 nPages
> 0 ? OUString() : maNoPageStr
,
1019 aPrt
->GetDPIX(), aPrt
->GetDPIY(),
1020 aPrt
->GetPrinterOptions().IsConvertToGreyscales()
1023 mxForwardBtn
->set_sensitive( mnCurPage
< nPages
-1 );
1024 mxBackwardBtn
->set_sensitive( mnCurPage
!= 0 );
1025 mxFirstBtn
->set_sensitive( mnCurPage
!= 0 );
1026 mxLastBtn
->set_sensitive( mnCurPage
< nPages
-1 );
1027 mxPageEdit
->set_sensitive( nPages
> 1 );
1030 void PrintDialog::updatePageRange(sal_Int32 nPages
)
1032 if (nPages
> 0 && !mxPageRangesRadioButton
->get_active())
1034 OUStringBuffer
aBuf(32);
1038 aBuf
.append("-" + OUString::number(nPages
));
1040 OUString sRange
= aBuf
.makeStringAndClear();
1041 mxPageRangeEdit
->set_text(sRange
);
1042 maPController
->setValue(u
"PageRange"_ustr
, Any(sRange
));
1046 void PrintDialog::updatePageSize(int nOrientation
)
1048 VclPtr
<Printer
> aPrt(maPController
->getPrinter());
1051 if (mxNupPagesBox
->get_active_id() == "1")
1053 PaperInfo aInfo
= aPrt
->GetPaperInfo(mxPaperSizeBox
->get_active());
1054 aSize
= Size(aInfo
.getWidth(), aInfo
.getHeight());
1055 if (aSize
.IsEmpty())
1056 aSize
= aPrt
->GetSizeOfPaper();
1058 if (nOrientation
!= ORIENTATION_AUTOMATIC
)
1060 if ((nOrientation
== ORIENTATION_PORTRAIT
&& aSize
.Width() > aSize
.Height())
1061 || (nOrientation
== ORIENTATION_LANDSCAPE
&& aSize
.Width() < aSize
.Height()))
1063 // coverity[swapped_arguments : FALSE] - this is in the intended order
1064 aSize
= Size(aSize
.Height(), aSize
.Width());
1069 aSize
= getJobPageSize();
1071 aPrt
->SetPrintPageSize(aSize
);
1072 aPrt
->SetUsePrintDialogSetting(true);
1075 void PrintDialog::updateOrientationBox( const bool bAutomatic
)
1079 Orientation eOrientation
= maPController
->getPrinter()->GetOrientation();
1080 mxOrientationBox
->set_active( static_cast<sal_Int32
>(eOrientation
) + 1 );
1082 else if ( hasOrientationChanged() )
1084 mxOrientationBox
->set_active( ORIENTATION_AUTOMATIC
);
1088 bool PrintDialog::hasOrientationChanged() const
1090 const int nOrientation
= mxOrientationBox
->get_active();
1091 const Orientation eOrientation
= maPController
->getPrinter()->GetOrientation();
1093 return (nOrientation
== ORIENTATION_LANDSCAPE
&& eOrientation
== Orientation::Portrait
)
1094 || (nOrientation
== ORIENTATION_PORTRAIT
&& eOrientation
== Orientation::Landscape
);
1097 // Always use this function to set paper orientation to make sure everything behaves well
1098 void PrintDialog::setPaperOrientation( Orientation eOrientation
, bool fromUser
)
1100 VclPtr
<Printer
> aPrt( maPController
->getPrinter() );
1101 aPrt
->SetOrientation( eOrientation
);
1102 maPController
->setOrientationFromUser( eOrientation
, fromUser
);
1105 void PrintDialog::checkControlDependencies()
1107 if (mxCopyCountField
->get_value() > 1)
1109 mxCollateBox
->set_sensitive( !mbCollateAlwaysOff
);
1110 mxSingleJobsBox
->set_sensitive( mxCollateBox
->get_active() );
1114 mxCollateBox
->set_sensitive( false );
1115 mxSingleJobsBox
->set_sensitive( false );
1118 OUString
aImg(mxCollateBox
->get_active() ? SV_PRINT_COLLATE_BMP
: SV_PRINT_NOCOLLATE_BMP
);
1120 mxCollateImage
->set_from_icon_name(aImg
);
1122 // enable setup button only for printers that can be setup
1123 bool bHaveSetup
= maPController
->getPrinter()->HasSupport( PrinterSupport::SetupDialog
);
1124 mxSetupButton
->set_sensitive(bHaveSetup
);
1127 void PrintDialog::checkOptionalControlDependencies()
1129 for( const auto& rEntry
: maControlToPropertyMap
)
1131 assert(rEntry
.first
);
1133 bool bShouldbeEnabled
= maPController
->isUIOptionEnabled( rEntry
.second
);
1135 if (bShouldbeEnabled
&& dynamic_cast<weld::RadioButton
*>(rEntry
.first
))
1137 auto r_it
= maControlToNumValMap
.find( rEntry
.first
);
1138 if( r_it
!= maControlToNumValMap
.end() )
1140 bShouldbeEnabled
= maPController
->isUIChoiceEnabled( rEntry
.second
, r_it
->second
);
1144 bool bIsEnabled
= rEntry
.first
->get_sensitive();
1145 // Enable does not do a change check first, so can be less cheap than expected
1146 if (bShouldbeEnabled
!= bIsEnabled
)
1147 rEntry
.first
->set_sensitive( bShouldbeEnabled
);
1151 void PrintDialog::initFromMultiPageSetup( const vcl::PrinterController::MultiPageSetup
& i_rMPS
)
1153 mxNupOrderWin
->show();
1154 mxPagesBtn
->set_active(true);
1155 mxBrochureBtn
->hide();
1157 // setup field units for metric fields
1158 const LocaleDataWrapper
& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
1159 FieldUnit eUnit
= FieldUnit::MM
;
1160 sal_uInt16 nDigits
= 0;
1161 if( rLocWrap
.getMeasurementSystemEnum() == MeasurementSystem::US
)
1163 eUnit
= FieldUnit::INCH
;
1167 mxPageMarginEdt
->set_unit( eUnit
);
1168 mxSheetMarginEdt
->set_unit( eUnit
);
1171 mxPageMarginEdt
->set_digits( nDigits
);
1172 mxSheetMarginEdt
->set_digits( nDigits
);
1174 mxSheetMarginEdt
->set_value( mxSheetMarginEdt
->normalize( i_rMPS
.nLeftMargin
), FieldUnit::MM_100TH
);
1175 mxPageMarginEdt
->set_value( mxPageMarginEdt
->normalize( i_rMPS
.nHorizontalSpacing
), FieldUnit::MM_100TH
);
1176 mxBorderCB
->set_active( i_rMPS
.bDrawBorder
);
1177 mxNupRowsEdt
->set_value( i_rMPS
.nRows
);
1178 mxNupColEdt
->set_value( i_rMPS
.nColumns
);
1179 mxNupOrderBox
->set_active( static_cast<sal_Int32
>(i_rMPS
.nOrder
) );
1180 if( i_rMPS
.nRows
!= 1 || i_rMPS
.nColumns
!= 1 )
1182 mxNupPagesBox
->set_active( mxNupPagesBox
->get_count()-1 );
1183 showAdvancedControls( true );
1184 mxNupOrder
->setValues( i_rMPS
.nOrder
, i_rMPS
.nColumns
, i_rMPS
.nRows
);
1188 void PrintDialog::updateNup( bool i_bMayUseCache
)
1190 int nRows
= mxNupRowsEdt
->get_value();
1191 int nCols
= mxNupColEdt
->get_value();
1192 tools::Long nPageMargin
= mxPageMarginEdt
->denormalize(mxPageMarginEdt
->get_value( FieldUnit::MM_100TH
));
1193 tools::Long nSheetMargin
= mxSheetMarginEdt
->denormalize(mxSheetMarginEdt
->get_value( FieldUnit::MM_100TH
));
1195 PrinterController::MultiPageSetup aMPS
;
1197 aMPS
.nColumns
= nCols
;
1201 aMPS
.nBottomMargin
= nSheetMargin
;
1203 aMPS
.nHorizontalSpacing
=
1204 aMPS
.nVerticalSpacing
= nPageMargin
;
1206 aMPS
.bDrawBorder
= mxBorderCB
->get_active();
1208 aMPS
.nOrder
= static_cast<NupOrderType
>(mxNupOrderBox
->get_active());
1210 int nOrientationMode
= mxOrientationBox
->get_active();
1211 if( nOrientationMode
== ORIENTATION_LANDSCAPE
)
1212 aMPS
.aPaperSize
= maNupLandscapeSize
;
1213 else if( nOrientationMode
== ORIENTATION_PORTRAIT
)
1214 aMPS
.aPaperSize
= maNupPortraitSize
;
1215 else // automatic mode
1217 // get size of first real page to see if it is portrait or landscape
1218 // we assume same page sizes for all the pages for this
1219 Size aPageSize
= getJobPageSize();
1221 Size
aMultiSize( aPageSize
.Width() * nCols
, aPageSize
.Height() * nRows
);
1222 if( aMultiSize
.Width() > aMultiSize
.Height() ) // fits better on landscape
1224 aMPS
.aPaperSize
= maNupLandscapeSize
;
1225 setPaperOrientation( Orientation::Landscape
, false );
1229 aMPS
.aPaperSize
= maNupPortraitSize
;
1230 setPaperOrientation( Orientation::Portrait
, false );
1234 maPController
->setMultipage( aMPS
);
1236 mxNupOrder
->setValues( aMPS
.nOrder
, nCols
, nRows
);
1239 maUpdatePreviewIdle
.Start();
1241 maUpdatePreviewNoCacheIdle
.Start();
1244 void PrintDialog::updateNupFromPages( bool i_bMayUseCache
)
1246 int nPages
= mxNupPagesBox
->get_active_id().toInt32();
1247 int nRows
= mxNupRowsEdt
->get_value();
1248 int nCols
= mxNupColEdt
->get_value();
1249 tools::Long nPageMargin
= mxPageMarginEdt
->denormalize(mxPageMarginEdt
->get_value( FieldUnit::MM_100TH
));
1250 tools::Long nSheetMargin
= mxSheetMarginEdt
->denormalize(mxSheetMarginEdt
->get_value( FieldUnit::MM_100TH
));
1251 bool bCustom
= false;
1259 else if( nPages
== 2 || nPages
== 4 || nPages
== 6 || nPages
== 9 || nPages
== 16 )
1261 Size
aJobPageSize( getJobPageSize() );
1262 bool bPortrait
= aJobPageSize
.Width() < aJobPageSize
.Height();
1276 else if( nPages
== 4 )
1278 else if( nPages
== 6 )
1291 else if( nPages
== 9 )
1293 else if( nPages
== 16 )
1303 // set upper limits for margins based on job page size and rows/columns
1304 Size
aSize( getJobPageSize() );
1306 // maximum sheet distance: 1/2 sheet
1307 tools::Long nHorzMax
= aSize
.Width()/2;
1308 tools::Long nVertMax
= aSize
.Height()/2;
1309 if( nSheetMargin
> nHorzMax
)
1310 nSheetMargin
= nHorzMax
;
1311 if( nSheetMargin
> nVertMax
)
1312 nSheetMargin
= nVertMax
;
1314 mxSheetMarginEdt
->set_max(
1315 mxSheetMarginEdt
->normalize(
1316 std::min(nHorzMax
, nVertMax
) ), FieldUnit::MM_100TH
);
1318 // maximum page distance
1319 nHorzMax
= (aSize
.Width() - 2*nSheetMargin
);
1321 nHorzMax
/= (nCols
-1);
1322 nVertMax
= (aSize
.Height() - 2*nSheetMargin
);
1324 nHorzMax
/= (nRows
-1);
1326 if( nPageMargin
> nHorzMax
)
1327 nPageMargin
= nHorzMax
;
1328 if( nPageMargin
> nVertMax
)
1329 nPageMargin
= nVertMax
;
1331 mxPageMarginEdt
->set_max(
1332 mxSheetMarginEdt
->normalize(
1333 std::min(nHorzMax
, nVertMax
) ), FieldUnit::MM_100TH
);
1336 mxNupRowsEdt
->set_value( nRows
);
1337 mxNupColEdt
->set_value( nCols
);
1338 mxPageMarginEdt
->set_value( mxPageMarginEdt
->normalize( nPageMargin
), FieldUnit::MM_100TH
);
1339 mxSheetMarginEdt
->set_value( mxSheetMarginEdt
->normalize( nSheetMargin
), FieldUnit::MM_100TH
);
1341 showAdvancedControls( bCustom
);
1342 updateNup( i_bMayUseCache
);
1345 void PrintDialog::enableNupControls( bool bEnable
)
1347 mxNupPagesBox
->set_sensitive( bEnable
);
1348 mxNupNumPagesTxt
->set_sensitive( bEnable
);
1349 mxNupColEdt
->set_sensitive( bEnable
);
1350 mxNupTimesTxt
->set_sensitive( bEnable
);
1351 mxNupRowsEdt
->set_sensitive( bEnable
);
1352 mxPageMarginTxt1
->set_sensitive( bEnable
);
1353 mxPageMarginEdt
->set_sensitive( bEnable
);
1354 mxPageMarginTxt2
->set_sensitive( bEnable
);
1355 mxSheetMarginTxt1
->set_sensitive( bEnable
);
1356 mxSheetMarginEdt
->set_sensitive( bEnable
);
1357 mxSheetMarginTxt2
->set_sensitive( bEnable
);
1358 mxNupOrderTxt
->set_sensitive( bEnable
);
1359 mxNupOrderBox
->set_sensitive( bEnable
);
1360 mxNupOrderWin
->set_sensitive( bEnable
);
1361 mxBorderCB
->set_sensitive( bEnable
);
1364 void PrintDialog::showAdvancedControls( bool i_bShow
)
1366 mxNupNumPagesTxt
->set_visible( i_bShow
);
1367 mxNupColEdt
->set_visible( i_bShow
);
1368 mxNupTimesTxt
->set_visible( i_bShow
);
1369 mxNupRowsEdt
->set_visible( i_bShow
);
1370 mxPageMarginTxt1
->set_visible( i_bShow
);
1371 mxPageMarginEdt
->set_visible( i_bShow
);
1372 mxPageMarginTxt2
->set_visible( i_bShow
);
1373 mxSheetMarginTxt1
->set_visible( i_bShow
);
1374 mxSheetMarginEdt
->set_visible( i_bShow
);
1375 mxSheetMarginTxt2
->set_visible( i_bShow
);
1380 void setHelpId( weld::Widget
* i_pWindow
, const Sequence
< OUString
>& i_rHelpIds
, sal_Int32 i_nIndex
)
1382 if( i_nIndex
>= 0 && i_nIndex
< i_rHelpIds
.getLength() )
1383 i_pWindow
->set_help_id( i_rHelpIds
.getConstArray()[i_nIndex
] );
1386 void setHelpText( weld::Widget
* i_pWindow
, const Sequence
< OUString
>& i_rHelpTexts
, sal_Int32 i_nIndex
)
1388 // without a help text set and the correct smartID,
1389 // help texts will be retrieved from the online help system
1390 if( i_nIndex
>= 0 && i_nIndex
< i_rHelpTexts
.getLength() )
1391 i_pWindow
->set_tooltip_text(i_rHelpTexts
.getConstArray()[i_nIndex
]);
1395 void PrintDialog::setupOptionalUI()
1397 const Sequence
< PropertyValue
>& rOptions( maPController
->getUIOptions() );
1398 for( const auto& rOption
: rOptions
)
1400 if (rOption
.Name
== "OptionsUIFile")
1402 OUString sOptionsUIFile
;
1403 rOption
.Value
>>= sOptionsUIFile
;
1404 mxCustomOptionsUIBuilder
= Application::CreateBuilder(mxCustom
.get(), sOptionsUIFile
);
1405 std::unique_ptr
<weld::Container
> xWindow
= mxCustomOptionsUIBuilder
->weld_container(u
"box"_ustr
);
1406 xWindow
->set_help_id(u
"vcl/ui/printdialog/PrintDialog"_ustr
);
1411 Sequence
< beans::PropertyValue
> aOptProp
;
1412 rOption
.Value
>>= aOptProp
;
1414 // extract ui element
1418 OUString aPropertyName
;
1419 Sequence
< OUString
> aChoices
;
1420 Sequence
< sal_Bool
> aChoicesDisabled
;
1421 Sequence
< OUString
> aHelpTexts
;
1422 Sequence
< OUString
> aIDs
;
1423 Sequence
< OUString
> aHelpIds
;
1424 sal_Int64 nMinValue
= 0, nMaxValue
= 0;
1425 OUString aGroupingHint
;
1427 for (const beans::PropertyValue
& rEntry
: aOptProp
)
1429 if ( rEntry
.Name
== "ID" )
1431 rEntry
.Value
>>= aIDs
;
1434 if ( rEntry
.Name
== "Text" )
1436 rEntry
.Value
>>= aText
;
1438 else if ( rEntry
.Name
== "ControlType" )
1440 rEntry
.Value
>>= aCtrlType
;
1442 else if ( rEntry
.Name
== "Choices" )
1444 rEntry
.Value
>>= aChoices
;
1446 else if ( rEntry
.Name
== "ChoicesDisabled" )
1448 rEntry
.Value
>>= aChoicesDisabled
;
1450 else if ( rEntry
.Name
== "Property" )
1453 rEntry
.Value
>>= aVal
;
1454 aPropertyName
= aVal
.Name
;
1456 else if ( rEntry
.Name
== "Enabled" )
1459 else if ( rEntry
.Name
== "GroupingHint" )
1461 rEntry
.Value
>>= aGroupingHint
;
1463 else if ( rEntry
.Name
== "DependsOnName" )
1466 else if ( rEntry
.Name
== "DependsOnEntry" )
1469 else if ( rEntry
.Name
== "AttachToDependency" )
1472 else if ( rEntry
.Name
== "MinValue" )
1474 rEntry
.Value
>>= nMinValue
;
1476 else if ( rEntry
.Name
== "MaxValue" )
1478 rEntry
.Value
>>= nMaxValue
;
1480 else if ( rEntry
.Name
== "HelpText" )
1482 if( ! (rEntry
.Value
>>= aHelpTexts
) )
1485 if( rEntry
.Value
>>= aHelpText
)
1487 aHelpTexts
.realloc( 1 );
1488 *aHelpTexts
.getArray() = aHelpText
;
1492 else if ( rEntry
.Name
== "HelpId" )
1494 if( ! (rEntry
.Value
>>= aHelpIds
) )
1497 if( rEntry
.Value
>>= aHelpId
)
1499 aHelpIds
.realloc( 1 );
1500 *aHelpIds
.getArray() = aHelpId
;
1504 else if ( rEntry
.Name
== "HintNoLayoutPage" )
1506 bool bHasLayoutFrame
= false;
1507 rEntry
.Value
>>= bHasLayoutFrame
;
1508 mbShowLayoutFrame
= !bHasLayoutFrame
;
1512 if (aCtrlType
== "Group")
1516 weld::Container
* pPage
= mxTabCtrl
->get_page(aID
);
1520 mxTabCtrl
->set_tab_label_text(aID
, aText
);
1523 if (aHelpIds
.hasElements())
1524 pPage
->set_help_id(aHelpIds
[0]);
1527 if (aHelpTexts
.hasElements())
1528 pPage
->set_tooltip_text(aHelpTexts
[0]);
1532 else if (aCtrlType
== "Subgroup" && !aID
.isEmpty())
1534 std::unique_ptr
<weld::Widget
> xWidget
;
1535 // since 'New Print Dialog Design' fromwhich in calc is not a frame anymore
1536 if (aID
== "fromwhich")
1538 std::unique_ptr
<weld::Label
> xLabel
= m_xBuilder
->weld_label(aID
);
1539 xLabel
->set_label(aText
);
1540 xWidget
= std::move(xLabel
);
1544 std::unique_ptr
<weld::Frame
> xFrame
= m_xBuilder
->weld_frame(aID
);
1545 if (!xFrame
&& mxCustomOptionsUIBuilder
)
1546 xFrame
= mxCustomOptionsUIBuilder
->weld_frame(aID
);
1549 xFrame
->set_label(aText
);
1550 xWidget
= std::move(xFrame
);
1558 setHelpId(xWidget
.get(), aHelpIds
, 0);
1560 setHelpText(xWidget
.get(), aHelpTexts
, 0);
1565 else if( aCtrlType
== "Bool" && aGroupingHint
== "LayoutPage" && aPropertyName
== "PrintProspect" )
1567 mxBrochureBtn
->set_label(aText
);
1568 mxBrochureBtn
->show();
1571 PropertyValue
* pVal
= maPController
->getValue( aPropertyName
);
1573 pVal
->Value
>>= bVal
;
1574 mxBrochureBtn
->set_active( bVal
);
1575 mxBrochureBtn
->set_sensitive( maPController
->isUIOptionEnabled( aPropertyName
) && pVal
!= nullptr );
1577 maPropertyToWindowMap
[aPropertyName
].emplace_back(mxBrochureBtn
.get());
1578 maControlToPropertyMap
[mxBrochureBtn
.get()] = aPropertyName
;
1581 setHelpId( mxBrochureBtn
.get(), aHelpIds
, 0 );
1583 setHelpText( mxBrochureBtn
.get(), aHelpTexts
, 0 );
1585 else if (aCtrlType
== "Bool")
1588 std::unique_ptr
<weld::CheckButton
> xNewBox
= m_xBuilder
->weld_check_button(aID
);
1589 if (!xNewBox
&& mxCustomOptionsUIBuilder
)
1590 xNewBox
= mxCustomOptionsUIBuilder
->weld_check_button(aID
);
1594 xNewBox
->set_label( aText
);
1598 PropertyValue
* pVal
= maPController
->getValue( aPropertyName
);
1600 pVal
->Value
>>= bVal
;
1601 xNewBox
->set_active( bVal
);
1602 xNewBox
->connect_toggled( LINK( this, PrintDialog
, UIOption_CheckHdl
) );
1604 maExtraControls
.emplace_back(std::move(xNewBox
));
1606 weld::Widget
* pWidget
= maExtraControls
.back().get();
1608 maPropertyToWindowMap
[aPropertyName
].emplace_back(pWidget
);
1609 maControlToPropertyMap
[pWidget
] = aPropertyName
;
1612 setHelpId(pWidget
, aHelpIds
, 0);
1614 setHelpText(pWidget
, aHelpTexts
, 0);
1616 else if (aCtrlType
== "Radio")
1618 sal_Int32 nCurHelpText
= 0;
1621 sal_Int32 nSelectVal
= 0;
1622 PropertyValue
* pVal
= maPController
->getValue( aPropertyName
);
1623 if( pVal
&& pVal
->Value
.hasValue() )
1624 pVal
->Value
>>= nSelectVal
;
1625 for( sal_Int32 m
= 0; m
< aChoices
.getLength(); m
++ )
1628 std::unique_ptr
<weld::RadioButton
> xBtn
= m_xBuilder
->weld_radio_button(aID
);
1629 if (!xBtn
&& mxCustomOptionsUIBuilder
)
1630 xBtn
= mxCustomOptionsUIBuilder
->weld_radio_button(aID
);
1634 xBtn
->set_label( aChoices
[m
] );
1635 xBtn
->set_active( m
== nSelectVal
);
1636 xBtn
->connect_toggled( LINK( this, PrintDialog
, UIOption_RadioHdl
) );
1637 if( aChoicesDisabled
.getLength() > m
&& aChoicesDisabled
[m
] )
1638 xBtn
->set_sensitive( false );
1641 maExtraControls
.emplace_back(std::move(xBtn
));
1643 weld::Widget
* pWidget
= maExtraControls
.back().get();
1645 maPropertyToWindowMap
[ aPropertyName
].emplace_back(pWidget
);
1646 maControlToPropertyMap
[pWidget
] = aPropertyName
;
1647 maControlToNumValMap
[pWidget
] = m
;
1650 setHelpId( pWidget
, aHelpIds
, nCurHelpText
);
1652 setHelpText( pWidget
, aHelpTexts
, nCurHelpText
);
1656 else if ( aCtrlType
== "List" )
1658 std::unique_ptr
<weld::ComboBox
> xList
= m_xBuilder
->weld_combo_box(aID
);
1659 if (!xList
&& mxCustomOptionsUIBuilder
)
1660 xList
= mxCustomOptionsUIBuilder
->weld_combo_box(aID
);
1665 for (const auto& rChoice
: aChoices
)
1666 xList
->append_text(rChoice
);
1668 sal_Int32 nSelectVal
= 0;
1669 PropertyValue
* pVal
= maPController
->getValue( aPropertyName
);
1670 if( pVal
&& pVal
->Value
.hasValue() )
1671 pVal
->Value
>>= nSelectVal
;
1672 xList
->set_active(nSelectVal
);
1673 xList
->connect_changed( LINK( this, PrintDialog
, UIOption_SelectHdl
) );
1676 maExtraControls
.emplace_back(std::move(xList
));
1678 weld::Widget
* pWidget
= maExtraControls
.back().get();
1680 maPropertyToWindowMap
[ aPropertyName
].emplace_back(pWidget
);
1681 maControlToPropertyMap
[pWidget
] = aPropertyName
;
1684 setHelpId( pWidget
, aHelpIds
, 0 );
1686 setHelpText( pWidget
, aHelpTexts
, 0 );
1688 else if ( aCtrlType
== "Range" )
1690 std::unique_ptr
<weld::SpinButton
> xField
= m_xBuilder
->weld_spin_button(aID
);
1691 if (!xField
&& mxCustomOptionsUIBuilder
)
1692 xField
= mxCustomOptionsUIBuilder
->weld_spin_button(aID
);
1696 // set min/max and current value
1697 if(nMinValue
!= nMaxValue
)
1698 xField
->set_range(nMinValue
, nMaxValue
);
1700 sal_Int64 nCurVal
= 0;
1701 PropertyValue
* pVal
= maPController
->getValue( aPropertyName
);
1702 if( pVal
&& pVal
->Value
.hasValue() )
1703 pVal
->Value
>>= nCurVal
;
1704 xField
->set_value( nCurVal
);
1705 xField
->connect_value_changed( LINK( this, PrintDialog
, UIOption_SpinModifyHdl
) );
1708 maExtraControls
.emplace_back(std::move(xField
));
1710 weld::Widget
* pWidget
= maExtraControls
.back().get();
1712 maPropertyToWindowMap
[ aPropertyName
].emplace_back(pWidget
);
1713 maControlToPropertyMap
[pWidget
] = aPropertyName
;
1716 setHelpId( pWidget
, aHelpIds
, 0 );
1718 setHelpText( pWidget
, aHelpTexts
, 0 );
1720 else if (aCtrlType
== "Edit")
1722 std::unique_ptr
<weld::Entry
> xField
= m_xBuilder
->weld_entry(aID
);
1723 if (!xField
&& mxCustomOptionsUIBuilder
)
1724 xField
= mxCustomOptionsUIBuilder
->weld_entry(aID
);
1729 PropertyValue
* pVal
= maPController
->getValue( aPropertyName
);
1730 if( pVal
&& pVal
->Value
.hasValue() )
1731 pVal
->Value
>>= aCurVal
;
1732 xField
->set_text( aCurVal
);
1733 xField
->connect_changed( LINK( this, PrintDialog
, UIOption_EntryModifyHdl
) );
1736 maExtraControls
.emplace_back(std::move(xField
));
1738 weld::Widget
* pWidget
= maExtraControls
.back().get();
1740 maPropertyToWindowMap
[ aPropertyName
].emplace_back(pWidget
);
1741 maControlToPropertyMap
[pWidget
] = aPropertyName
;
1744 setHelpId( pWidget
, aHelpIds
, 0 );
1746 setHelpText( pWidget
, aHelpTexts
, 0 );
1750 SAL_WARN( "vcl", "Unsupported UI option: \"" << aCtrlType
<< '"');
1754 // #i106506# if no brochure button, then the singular Pages radio button
1755 // makes no sense, so replace it by a FixedText label
1756 if (!mxBrochureBtn
->get_visible() && mxPagesBtn
->get_visible())
1758 mxPagesBoxTitleTxt
->set_label(mxPagesBtn
->get_label());
1759 mxPagesBoxTitleTxt
->show();
1762 mxNupPagesBox
->set_accessible_relation_labeled_by(mxPagesBoxTitleTxt
.get());
1765 // update enable states
1766 checkOptionalControlDependencies();
1768 // print range not shown (currently math only) -> hide spacer line and reverse order
1769 if (!mxPageRangeEdit
->get_visible())
1771 mxReverseOrderBox
->hide();
1774 if (!mxCustomOptionsUIBuilder
)
1775 mxTabCtrl
->remove_page(mxTabCtrl
->get_page_ident(1));
1778 void PrintDialog::makeEnabled( weld::Widget
* i_pWindow
)
1780 auto it
= maControlToPropertyMap
.find( i_pWindow
);
1781 if( it
!= maControlToPropertyMap
.end() )
1783 OUString
aDependency( maPController
->makeEnabled( it
->second
) );
1784 if( !aDependency
.isEmpty() )
1785 updateWindowFromProperty( aDependency
);
1789 void PrintDialog::updateWindowFromProperty( const OUString
& i_rProperty
)
1791 beans::PropertyValue
* pValue
= maPController
->getValue( i_rProperty
);
1792 auto it
= maPropertyToWindowMap
.find( i_rProperty
);
1793 if( !(pValue
&& it
!= maPropertyToWindowMap
.end()) )
1796 const auto& rWindows( it
->second
);
1797 if( rWindows
.empty() )
1801 sal_Int32 nVal
= -1;
1802 if( pValue
->Value
>>= bVal
)
1804 // we should have a CheckBox for this one
1805 weld::CheckButton
* pBox
= dynamic_cast<weld::CheckButton
*>(rWindows
.front());
1808 pBox
->set_active( bVal
);
1810 else if ( i_rProperty
== "PrintProspect" )
1812 // EVIL special case
1814 mxBrochureBtn
->set_active(true);
1816 mxPagesBtn
->set_active(true);
1820 SAL_WARN( "vcl", "missing a checkbox" );
1823 else if( pValue
->Value
>>= nVal
)
1825 // this could be a ListBox or a RadioButtonGroup
1826 weld::ComboBox
* pList
= dynamic_cast<weld::ComboBox
*>(rWindows
.front());
1829 pList
->set_active( static_cast< sal_uInt16
>(nVal
) );
1831 else if( nVal
>= 0 && o3tl::make_unsigned(nVal
) < rWindows
.size() )
1833 weld::RadioButton
* pBtn
= dynamic_cast<weld::RadioButton
*>(rWindows
[nVal
]);
1834 SAL_WARN_IF( !pBtn
, "vcl", "unexpected control for property" );
1836 pBtn
->set_active(true);
1841 bool PrintDialog::isPrintToFile() const
1843 return ( mxPrinters
->get_active() == 0 );
1846 bool PrintDialog::isCollate() const
1848 return mxCopyCountField
->get_value() > 1 && mxCollateBox
->get_active();
1851 bool PrintDialog::isSingleJobs() const
1853 return mxSingleJobsBox
->get_active();
1856 bool PrintDialog::hasPreview() const
1858 return mxPreviewBox
->get_active();
1861 PropertyValue
* PrintDialog::getValueForWindow( weld::Widget
* i_pWindow
) const
1863 PropertyValue
* pVal
= nullptr;
1864 auto it
= maControlToPropertyMap
.find( i_pWindow
);
1865 if( it
!= maControlToPropertyMap
.end() )
1867 pVal
= maPController
->getValue( it
->second
);
1868 SAL_WARN_IF( !pVal
, "vcl", "property value not found" );
1872 OSL_FAIL( "changed control not in property map" );
1877 IMPL_LINK(PrintDialog
, ToggleHdl
, weld::Toggleable
&, rButton
, void)
1879 if (&rButton
== mxPreviewBox
.get())
1881 maUpdatePreviewIdle
.Start();
1883 else if( &rButton
== mxBorderCB
.get() )
1887 else if (&rButton
== mxSingleJobsBox
.get())
1889 maPController
->setValue( u
"SinglePrintJobs"_ustr
,
1890 Any( isSingleJobs() ) );
1891 checkControlDependencies();
1893 else if( &rButton
== mxCollateBox
.get() )
1895 maPController
->setValue( u
"Collate"_ustr
,
1896 Any( isCollate() ) );
1897 checkControlDependencies();
1899 else if( &rButton
== mxReverseOrderBox
.get() )
1901 bool bChecked
= mxReverseOrderBox
->get_active();
1902 maPController
->setReversePrint( bChecked
);
1903 maPController
->setValue( u
"PrintReverse"_ustr
,
1905 maUpdatePreviewIdle
.Start();
1907 else if (&rButton
== mxBrochureBtn
.get())
1909 PropertyValue
* pVal
= getValueForWindow(mxBrochureBtn
.get());
1912 bool bVal
= mxBrochureBtn
->get_active();
1913 pVal
->Value
<<= bVal
;
1915 checkOptionalControlDependencies();
1917 // update preview and page settings
1918 maUpdatePreviewNoCacheIdle
.Start();
1920 if (mxBrochureBtn
->get_active())
1922 mxOrientationBox
->set_sensitive( false );
1923 mxOrientationBox
->set_active( ORIENTATION_LANDSCAPE
);
1924 mxNupPagesBox
->set_active( 0 );
1925 updateNupFromPages();
1926 showAdvancedControls( false );
1927 enableNupControls( false );
1931 mxOrientationBox
->set_sensitive( true );
1932 mxOrientationBox
->set_active( ORIENTATION_AUTOMATIC
);
1933 updatePageSize(mxOrientationBox
->get_active());
1934 enableNupControls( true );
1935 updateNupFromPages();
1941 IMPL_LINK(PrintDialog
, ClickHdl
, weld::Button
&, rButton
, void)
1943 if (&rButton
== mxOKButton
.get() || &rButton
== mxCancelButton
.get())
1946 m_xDialog
->response(&rButton
== mxOKButton
.get() ? RET_OK
: RET_CANCEL
);
1948 else if( &rButton
== mxForwardBtn
.get() )
1952 else if( &rButton
== mxBackwardBtn
.get() )
1956 else if( &rButton
== mxFirstBtn
.get() )
1960 else if( &rButton
== mxLastBtn
.get() )
1966 if( &rButton
== mxSetupButton
.get() )
1968 maPController
->setupPrinter(m_xDialog
.get());
1970 if ( !isPrintToFile() )
1972 VclPtr
<Printer
> aPrt( maPController
->getPrinter() );
1973 mePaper
= aPrt
->GetPaper();
1975 for (int nPaper
= 0; nPaper
< aPrt
->GetPaperInfoCount(); nPaper
++ )
1977 PaperInfo aInfo
= aPrt
->GetPaperInfo( nPaper
);
1978 aInfo
.doSloppyFit(true);
1979 Paper ePaper
= aInfo
.getPaper();
1981 if ( mePaper
== ePaper
)
1983 mxPaperSizeBox
->set_active( nPaper
);
1989 updateOrientationBox( false );
1991 updatePageSize(mxOrientationBox
->get_active());
1993 setupPaperSidesBox();
1995 // tdf#63905 don't use cache: page size may change
1996 maUpdatePreviewNoCacheIdle
.Start();
1998 checkControlDependencies();
2003 IMPL_LINK( PrintDialog
, SelectHdl
, weld::ComboBox
&, rBox
, void )
2005 if (&rBox
== mxPrinters
.get())
2007 if ( !isPrintToFile() )
2009 OUString
aNewPrinter(rBox
.get_active_text());
2011 maPController
->setPrinter( VclPtrInstance
<Printer
>( aNewPrinter
) );
2012 maPController
->resetPrinterOptions( false );
2013 // invalidate page cache and start fresh
2014 maPController
->invalidatePageCache();
2015 maFirstPageSize
= Size();
2017 updateOrientationBox();
2018 updatePageSize(mxOrientationBox
->get_active());
2020 // update text fields
2021 mxOKButton
->set_label(maPrintText
);
2022 updatePrinterText();
2025 maUpdatePreviewIdle
.Start();
2027 else // print to file
2029 // use the default printer or FIXME: the last used one?
2030 maPController
->setPrinter( VclPtrInstance
<Printer
>( Printer::GetDefaultPrinterName() ) );
2031 mxOKButton
->set_label(maPrintToFileText
);
2032 maPController
->resetPrinterOptions( true );
2035 updateOrientationBox();
2036 updatePageSize(mxOrientationBox
->get_active());
2037 maUpdatePreviewIdle
.Start();
2040 setupPaperSidesBox();
2042 // Related: tdf#159995 changing a listbox's active item does not
2043 // trigger an update to the preview so force an update to the
2044 // preview by calling each active listbox's select handler after
2045 // changing the active printer.
2046 if ( mxOrientationBox
->get_sensitive() )
2047 LINK( this, PrintDialog
, SelectHdl
).Call( *mxOrientationBox
);
2048 if ( mxPaperSizeBox
->get_sensitive() )
2049 LINK( this, PrintDialog
, SelectHdl
).Call( *mxPaperSizeBox
);
2051 else if ( &rBox
== mxPaperSidesBox
.get() )
2053 DuplexMode eDuplex
= static_cast<DuplexMode
>(mxPaperSidesBox
->get_active() + 1);
2054 maPController
->getPrinter()->SetDuplexMode( eDuplex
);
2056 else if( &rBox
== mxOrientationBox
.get() )
2058 int nOrientation
= mxOrientationBox
->get_active();
2059 if ( nOrientation
!= ORIENTATION_AUTOMATIC
)
2060 setPaperOrientation( static_cast<Orientation
>( nOrientation
- 1 ), true );
2062 updatePageSize(nOrientation
);
2065 else if ( &rBox
== mxNupOrderBox
.get() )
2069 else if( &rBox
== mxNupPagesBox
.get() )
2071 if( !mxPagesBtn
->get_active() )
2072 mxPagesBtn
->set_active(true);
2073 updatePageSize(mxOrientationBox
->get_active());
2074 updateNupFromPages( false );
2076 else if ( &rBox
== mxPaperSizeBox
.get() )
2078 VclPtr
<Printer
> aPrt( maPController
->getPrinter() );
2079 PaperInfo aInfo
= aPrt
->GetPaperInfo( rBox
.get_active() );
2080 aInfo
.doSloppyFit(true);
2081 mePaper
= aInfo
.getPaper();
2083 if ( mePaper
== PAPER_USER
)
2084 aPrt
->SetPaperSizeUser( Size( aInfo
.getWidth(), aInfo
.getHeight() ) );
2086 aPrt
->SetPaper( mePaper
);
2088 maPController
->setPaperSizeFromUser( Size( aInfo
.getWidth(), aInfo
.getHeight() ) );
2090 updatePageSize(mxOrientationBox
->get_active());
2092 maUpdatePreviewNoCacheIdle
.Start();
2096 IMPL_LINK_NOARG(PrintDialog
, MetricSpinModifyHdl
, weld::MetricSpinButton
&, void)
2098 checkControlDependencies();
2099 updateNupFromPages();
2102 IMPL_LINK_NOARG(PrintDialog
, FocusOutHdl
, weld::Widget
&, void)
2104 ActivateHdl(*mxPageEdit
);
2107 IMPL_LINK_NOARG(PrintDialog
, ActivateHdl
, weld::Entry
&, bool)
2109 sal_Int32 nPage
= mxPageEdit
->get_text().toInt32();
2113 mxPageEdit
->set_text(u
"1"_ustr
);
2115 else if (nPage
> mnCachedPages
)
2117 nPage
= mnCachedPages
;
2118 mxPageEdit
->set_text(OUString::number(mnCachedPages
));
2120 int nNewCurPage
= nPage
- 1;
2121 if (nNewCurPage
!= mnCurPage
)
2123 mnCurPage
= nNewCurPage
;
2124 maUpdatePreviewIdle
.Start();
2129 IMPL_LINK( PrintDialog
, SpinModifyHdl
, weld::SpinButton
&, rEdit
, void )
2131 checkControlDependencies();
2132 if (&rEdit
== mxNupRowsEdt
.get() || &rEdit
== mxNupColEdt
.get())
2134 updateNupFromPages();
2136 else if( &rEdit
== mxCopyCountField
.get() )
2138 maPController
->setValue( u
"CopyCount"_ustr
,
2139 Any( sal_Int32(mxCopyCountField
->get_value()) ) );
2140 maPController
->setValue( u
"Collate"_ustr
,
2141 Any( isCollate() ) );
2145 IMPL_LINK( PrintDialog
, UIOption_CheckHdl
, weld::Toggleable
&, i_rBox
, void )
2147 PropertyValue
* pVal
= getValueForWindow( &i_rBox
);
2150 makeEnabled( &i_rBox
);
2152 bool bVal
= i_rBox
.get_active();
2153 pVal
->Value
<<= bVal
;
2155 checkOptionalControlDependencies();
2157 // update preview and page settings
2158 maUpdatePreviewNoCacheIdle
.Start();
2162 IMPL_LINK( PrintDialog
, UIOption_RadioHdl
, weld::Toggleable
&, i_rBtn
, void )
2164 // this handler gets called for all radiobuttons that get unchecked, too
2165 // however we only want one notification for the new value (that is for
2166 // the button that gets checked)
2167 if( !i_rBtn
.get_active() )
2170 PropertyValue
* pVal
= getValueForWindow( &i_rBtn
);
2171 auto it
= maControlToNumValMap
.find( &i_rBtn
);
2172 if( !(pVal
&& it
!= maControlToNumValMap
.end()) )
2175 makeEnabled( &i_rBtn
);
2177 sal_Int32 nVal
= it
->second
;
2178 pVal
->Value
<<= nVal
;
2180 updateOrientationBox();
2182 checkOptionalControlDependencies();
2184 // tdf#41205 give focus to the page range edit if the corresponding radio button was selected
2185 if (pVal
->Name
== "PrintContent" && mxPageRangesRadioButton
->get_active())
2186 mxPageRangeEdit
->grab_focus();
2188 // update preview and page settings
2189 maUpdatePreviewNoCacheIdle
.Start();
2192 IMPL_LINK( PrintDialog
, UIOption_SelectHdl
, weld::ComboBox
&, i_rBox
, void )
2194 PropertyValue
* pVal
= getValueForWindow( &i_rBox
);
2198 makeEnabled( &i_rBox
);
2200 sal_Int32
nVal( i_rBox
.get_active() );
2201 pVal
->Value
<<= nVal
;
2203 //If we are in impress we start in print slides mode and get a
2204 //maFirstPageSize for slides which are usually landscape mode, if we
2205 //change to notes which are usually in portrait mode, and then visit
2206 //n-up print, we will assume notes are in landscape unless we throw
2207 //away maFirstPageSize when we change page content type
2208 if (pVal
->Name
== "PageContentType")
2210 maFirstPageSize
= Size();
2212 css::uno::Sequence
<sal_Bool
> aChoicesDisabled
{
2213 false, // Original size
2214 false, // Fit to printable page
2215 (nVal
== 2) /*Notes*/ ? true : false, // disable/enable Multiple sheets of paper
2216 (nVal
== 2) /*Notes*/ ? true : false // disable/enable Tile sheet of paper
2218 maPController
->setUIChoicesDisabled(u
"PageOptions"_ustr
, aChoicesDisabled
);
2221 checkOptionalControlDependencies();
2223 updatePageSize(mxOrientationBox
->get_active());
2225 // update preview and page settings
2226 maUpdatePreviewNoCacheIdle
.Start();
2229 IMPL_LINK( PrintDialog
, UIOption_SpinModifyHdl
, weld::SpinButton
&, i_rBox
, void )
2231 PropertyValue
* pVal
= getValueForWindow( &i_rBox
);
2234 makeEnabled( &i_rBox
);
2236 sal_Int64 nVal
= i_rBox
.get_value();
2237 pVal
->Value
<<= nVal
;
2239 checkOptionalControlDependencies();
2241 // update preview and page settings
2242 maUpdatePreviewNoCacheIdle
.Start();
2246 IMPL_LINK( PrintDialog
, UIOption_EntryModifyHdl
, weld::Entry
&, i_rBox
, void )
2248 PropertyValue
* pVal
= getValueForWindow( &i_rBox
);
2249 if( pVal
&& mxPageRangesRadioButton
->get_active() )
2251 makeEnabled( &i_rBox
);
2253 OUString
aVal( i_rBox
.get_text() );
2254 pVal
->Value
<<= aVal
;
2256 checkOptionalControlDependencies();
2258 // update preview and page settings
2259 maUpdatePreviewNoCacheIdle
.Start();
2263 void PrintDialog::previewForward()
2265 sal_Int32 nValue
= mxPageEdit
->get_text().toInt32() + 1;
2266 if (nValue
<= mnCachedPages
)
2268 mxPageEdit
->set_text(OUString::number(nValue
));
2269 ActivateHdl(*mxPageEdit
);
2273 void PrintDialog::previewBackward()
2275 sal_Int32 nValue
= mxPageEdit
->get_text().toInt32() - 1;
2278 mxPageEdit
->set_text(OUString::number(nValue
));
2279 ActivateHdl(*mxPageEdit
);
2283 void PrintDialog::previewFirst()
2285 mxPageEdit
->set_text(u
"1"_ustr
);
2286 ActivateHdl(*mxPageEdit
);
2289 void PrintDialog::previewLast()
2291 mxPageEdit
->set_text(OUString::number(mnCachedPages
));
2292 ActivateHdl(*mxPageEdit
);
2296 static OUString
getNewLabel(const OUString
& aLabel
, int i_nCurr
, int i_nMax
)
2298 OUString
aNewText( aLabel
.replaceFirst( "%p", OUString::number( i_nCurr
) ) );
2299 aNewText
= aNewText
.replaceFirst( "%n", OUString::number( i_nMax
) );
2304 // PrintProgressDialog
2305 PrintProgressDialog::PrintProgressDialog(weld::Window
* i_pParent
, int i_nMax
)
2306 : GenericDialogController(i_pParent
, u
"vcl/ui/printprogressdialog.ui"_ustr
, u
"PrintProgressDialog"_ustr
)
2310 , mxText(m_xBuilder
->weld_label(u
"label"_ustr
))
2311 , mxProgress(m_xBuilder
->weld_progress_bar(u
"progressbar"_ustr
))
2312 , mxButton(m_xBuilder
->weld_button(u
"cancel"_ustr
))
2317 maStr
= mxText
->get_label();
2319 //just multiply largest value by 10 and take the width of that string as
2320 //the max size we will want
2321 mxText
->set_label(getNewLabel(maStr
, mnMax
* 10, mnMax
* 10));
2322 mxText
->set_size_request(mxText
->get_preferred_size().Width(), -1);
2324 //Pick a useful max width
2325 mxProgress
->set_size_request(mxProgress
->get_approximate_digit_width() * 25, -1);
2327 mxButton
->connect_clicked( LINK( this, PrintProgressDialog
, ClickHdl
) );
2329 // after this patch f7157f04fab298423e2c4f6a7e5f8e361164b15f, we have seen the calc Max string (sometimes) look above
2330 // now init to the right start values
2331 mxText
->set_label(getNewLabel(maStr
, mnCur
, mnMax
));
2334 PrintProgressDialog::~PrintProgressDialog()
2338 IMPL_LINK_NOARG(PrintProgressDialog
, ClickHdl
, weld::Button
&, void)
2343 void PrintProgressDialog::setProgress( int i_nCurrent
)
2350 mxText
->set_label(getNewLabel(maStr
, mnCur
, mnMax
));
2352 // here view the dialog, with the right label
2353 mxProgress
->set_percentage(mnCur
*100/mnMax
);
2356 void PrintProgressDialog::tick()
2359 setProgress( ++mnCur
);
2362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */