/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace vcl; using namespace com::sun::star; using namespace com::sun::star::uno; using namespace com::sun::star::lang; using namespace com::sun::star::container; using namespace com::sun::star::beans; extern "C" SAL_DLLPUBLIC_EXPORT void makePrintPreviewWindow(VclPtr & rRet, VclPtr & pParent, VclBuilder::stringmap &) { rRet = VclPtr::Create(pParent); } extern "C" SAL_DLLPUBLIC_EXPORT void makeShowNupOrderWindow(VclPtr & rRet, VclPtr & pParent, VclBuilder::stringmap &) { rRet = VclPtr::Create(pParent); } PrintDialog::PrintPreviewWindow::PrintPreviewWindow( vcl::Window* i_pParent ) : Window( i_pParent, 0 ) , maMtf() , maOrigSize( 10, 10 ) , maPreviewSize() , mnDPIX(Application::GetDefaultDevice()->GetDPIX()) , mnDPIY(Application::GetDefaultDevice()->GetDPIY()) , maPreviewBitmap() , maReplacementString() , maToolTipString(VclResId( SV_PRINT_PRINTPREVIEW_TXT)) , mbGreyscale( false ) , maHorzDim(VclPtr::Create(this, WB_HORZ | WB_CENTER)) , maVertDim(VclPtr::Create(this, WB_VERT | WB_VCENTER)) { SetPaintTransparent( true ); SetBackground(); maHorzDim->Show(); maVertDim->Show(); maHorzDim->SetText( "2.0in" ); maVertDim->SetText( "2.0in" ); } PrintDialog::PrintPreviewWindow::~PrintPreviewWindow() { disposeOnce(); } void PrintDialog::PrintPreviewWindow::dispose() { maHorzDim.disposeAndClear(); maVertDim.disposeAndClear(); Window::dispose(); } void PrintDialog::PrintPreviewWindow::Resize() { Size aNewSize( GetSizePixel() ); long nTextHeight = maHorzDim->GetTextHeight(); // leave small space for decoration aNewSize.AdjustWidth( -(nTextHeight + 2) ); aNewSize.AdjustHeight( -(nTextHeight + 2) ); Size aScaledSize; double fScale = 1.0; // #i106435# catch corner case of Size(0,0) Size aOrigSize( maOrigSize ); if( aOrigSize.Width() < 1 ) aOrigSize.setWidth( aNewSize.Width() ); if( aOrigSize.Height() < 1 ) aOrigSize.setHeight( aNewSize.Height() ); if( aOrigSize.Width() > aOrigSize.Height() ) { aScaledSize = Size( aNewSize.Width(), aNewSize.Width() * aOrigSize.Height() / aOrigSize.Width() ); if( aScaledSize.Height() > aNewSize.Height() ) fScale = double(aNewSize.Height())/double(aScaledSize.Height()); } else { aScaledSize = Size( aNewSize.Height() * aOrigSize.Width() / aOrigSize.Height(), aNewSize.Height() ); if( aScaledSize.Width() > aNewSize.Width() ) fScale = double(aNewSize.Width())/double(aScaledSize.Width()); } aScaledSize.setWidth( long(aScaledSize.Width()*fScale) ); aScaledSize.setHeight( long(aScaledSize.Height()*fScale) ); maPreviewSize = aScaledSize; // position dimension lines Point aRef( nTextHeight + (aNewSize.Width() - maPreviewSize.Width())/2, nTextHeight + (aNewSize.Height() - maPreviewSize.Height())/2 ); maHorzDim->SetPosSizePixel( Point( aRef.X(), aRef.Y() - nTextHeight ), Size( maPreviewSize.Width(), nTextHeight ) ); maVertDim->SetPosSizePixel( Point( aRef.X() - nTextHeight, aRef.Y() ), Size( nTextHeight, maPreviewSize.Height() ) ); // check and evtl. recreate preview bitmap preparePreviewBitmap(); } void PrintDialog::PrintPreviewWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) { long nTextHeight = maHorzDim->GetTextHeight(); Size aSize(GetSizePixel()); Point aOffset((aSize.Width() - maPreviewSize.Width() + nTextHeight) / 2, (aSize.Height() - maPreviewSize.Height() + nTextHeight) / 2); if (!maReplacementString.isEmpty()) { // replacement is active rRenderContext.Push(); Font aFont(rRenderContext.GetSettings().GetStyleSettings().GetLabelFont()); SetZoomedPointFont(rRenderContext, aFont); tools::Rectangle aTextRect(aOffset + Point(2, 2), Size(maPreviewSize.Width() - 4, maPreviewSize.Height() - 4)); rRenderContext.DrawText(aTextRect, maReplacementString, DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::WordBreak | DrawTextFlags::MultiLine); rRenderContext.Pop(); } else { Bitmap aPreviewBitmap(maPreviewBitmap); // This explicit force-to-scale allows us to get the // mentioned best quality here. Unfortunately this is // currently not sure when using just ::DrawBitmap with // a defined size or ::DrawOutDev aPreviewBitmap.Scale(maPreviewSize, BmpScaleFlag::BestQuality); rRenderContext.DrawBitmap(aOffset, aPreviewBitmap); } tools::Rectangle aFrameRect(aOffset + Point(-1, -1), Size(maPreviewSize.Width() + 2, maPreviewSize.Height() + 2)); DecorationView aDecorationView(&rRenderContext); aDecorationView.DrawFrame(aFrameRect, DrawFrameStyle::Group); } void PrintDialog::PrintPreviewWindow::Command( const CommandEvent& rEvt ) { if( rEvt.GetCommand() == CommandEventId::Wheel ) { const CommandWheelData* pWheelData = rEvt.GetWheelData(); PrintDialog* pDlg = dynamic_cast(GetParentDialog()); if( pDlg ) { if( pWheelData->GetDelta() > 0 ) pDlg->previewForward(); else if( pWheelData->GetDelta() < 0 ) pDlg->previewBackward(); } } } void PrintDialog::PrintPreviewWindow::setPreview( const GDIMetaFile& i_rNewPreview, const Size& i_rOrigSize, const OUString& i_rPaperName, const OUString& i_rReplacement, sal_Int32 i_nDPIX, sal_Int32 i_nDPIY, bool i_bGreyscale ) { OUStringBuffer aBuf( 256 ); aBuf.append( maToolTipString ); SetQuickHelpText( aBuf.makeStringAndClear() ); maMtf = i_rNewPreview; mnDPIX = i_nDPIX; mnDPIY = i_nDPIY; maOrigSize = i_rOrigSize; maReplacementString = i_rReplacement; mbGreyscale = i_bGreyscale; // use correct measurements const LocaleDataWrapper& rLocWrap( GetSettings().GetLocaleDataWrapper() ); MapUnit eUnit = MapUnit::MapMM; int nDigits = 0; if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US ) { eUnit = MapUnit::Map100thInch; nDigits = 2; } Size aLogicPaperSize( LogicToLogic( i_rOrigSize, MapMode( MapUnit::Map100thMM ), MapMode( eUnit ) ) ); OUString aNumText( rLocWrap.getNum( aLogicPaperSize.Width(), nDigits ) ); aBuf.append( aNumText ) .append( u' ' ); aBuf.appendAscii( eUnit == MapUnit::MapMM ? "mm" : "in" ); if( !i_rPaperName.isEmpty() ) { aBuf.append( " (" ); aBuf.append( i_rPaperName ); aBuf.append( ')' ); } maHorzDim->SetText( aBuf.makeStringAndClear() ); aNumText = rLocWrap.getNum( aLogicPaperSize.Height(), nDigits ); aBuf.append( aNumText ) .append( u' ' ); aBuf.appendAscii( eUnit == MapUnit::MapMM ? "mm" : "in" ); maVertDim->SetText( aBuf.makeStringAndClear() ); // We have a new Metafile and evtl. a new page, so we need to reset // the PreviewBitmap to force new creation maPreviewBitmap = Bitmap(); // sets/calculates e.g. maPreviewSize // also triggers preparePreviewBitmap() Resize(); Invalidate(); } void PrintDialog::PrintPreviewWindow::preparePreviewBitmap() { if(maPreviewSize.getWidth() < 0 || maPreviewSize.getHeight() < 0) { // not yet fully initialized, no need to prepare anything return; } // define an allowed number of pixels, also see // defaults for primitive renderers and similar. This // might be centralized and made dependent of 32/64bit const sal_uInt32 nMaxSquarePixels(500000); // check how big (squarePixels) the preview is currently (with // max value of MaxSquarePixels) const sal_uInt32 nCurrentSquarePixels( std::min( nMaxSquarePixels, static_cast(maPreviewBitmap.GetSizePixel().getWidth()) * static_cast(maPreviewBitmap.GetSizePixel().getHeight()))); // check how big (squarePixels) the preview needs to be (with // max value of MaxSquarePixels) const sal_uInt32 nRequiredSquarePixels( std::min( nMaxSquarePixels, static_cast(maPreviewSize.getWidth()) * static_cast(maPreviewSize.getHeight()))); // check if preview is big enough. Use a scaling value in // the comparison to not get bigger at the last possible moment // what may look awkward and pixelated (again). This means // to use a percentage value - if we have at least // that value of required pixels, we are good. static double fPreventAwkwardFactor(1.35); // 35% if(nCurrentSquarePixels >= static_cast(nRequiredSquarePixels * fPreventAwkwardFactor)) { // at this place we also could add a mechanism to let the preview // bitmap 'shrink' again if it is currently 'too big' -> bigger // than required. I think this is not necessary for now. // already sufficient, done. return; } // check if we have enough square pixels e.g for 8x8 pixels if(nRequiredSquarePixels < 64) { // too small preview - let it empty return; } // Calculate nPlannedSquarePixels which is the required size // expanded by a percentage (with max value of MaxSquarePixels) static double fExtraSpaceFactor(1.65); // 65% const sal_uInt32 nPlannedSquarePixels( std::min( nMaxSquarePixels, static_cast(maPreviewSize.getWidth() * fExtraSpaceFactor) * static_cast(maPreviewSize.getHeight() * fExtraSpaceFactor))); // calculate back new width and height - it might have been // truncated by MaxSquarePixels. // We know that w*h == nPlannedSquarePixels and w/h == ratio const double fRatio(static_cast(maPreviewSize.getWidth()) / static_cast(maPreviewSize.getHeight())); const double fNewWidth(sqrt(static_cast(nPlannedSquarePixels) * fRatio)); const double fNewHeight(sqrt(static_cast(nPlannedSquarePixels) / fRatio)); const Size aScaledSize(basegfx::fround(fNewWidth), basegfx::fround(fNewHeight)); // check if this eventual maximum is already reached // due to having hit the MaxSquarePixels. Due to using // an integer AspectRatio, we cannot make a numeric exact // comparison - we need to compare if we are close const double fScaledSizeSquare(static_cast(aScaledSize.getWidth() * aScaledSize.getHeight())); const double fPreviewSizeSquare(static_cast(maPreviewBitmap.GetSizePixel().getWidth() * maPreviewBitmap.GetSizePixel().getHeight())); // test as equal up to 0.1% (0.001) if(fPreviewSizeSquare != 0.0 && fabs((fScaledSizeSquare / fPreviewSizeSquare) - 1.0) < 0.001) { // maximum is reached, avoid bigger scaling return; } // create temporary VDev and render to it ScopedVclPtrInstance pPrerenderVDev(*Application::GetDefaultDevice()); pPrerenderVDev->SetOutputSizePixel(aScaledSize, false); pPrerenderVDev->SetReferenceDevice( mnDPIX, mnDPIY ); pPrerenderVDev->EnableOutput(); pPrerenderVDev->SetBackground( Wallpaper(COL_WHITE) ); GDIMetaFile aMtf( maMtf ); Size aVDevSize( pPrerenderVDev->GetOutputSizePixel() ); const Size aLogicSize( pPrerenderVDev->PixelToLogic( aVDevSize, MapMode( MapUnit::Map100thMM ) ) ); Size aOrigSize( maOrigSize ); if( aOrigSize.Width() < 1 ) aOrigSize.setWidth( aLogicSize.Width() ); if( aOrigSize.Height() < 1 ) aOrigSize.setHeight( aLogicSize.Height() ); double fScale = double(aLogicSize.Width())/double(aOrigSize.Width()); pPrerenderVDev->Erase(); pPrerenderVDev->Push(); pPrerenderVDev->SetMapMode(MapMode(MapUnit::Map100thMM)); DrawModeFlags nOldDrawMode = pPrerenderVDev->GetDrawMode(); if( mbGreyscale ) pPrerenderVDev->SetDrawMode( pPrerenderVDev->GetDrawMode() | ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText | DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) ); aMtf.WindStart(); aMtf.Scale( fScale, fScale ); aMtf.WindStart(); const AntialiasingFlags nOriginalAA(pPrerenderVDev->GetAntialiasing()); pPrerenderVDev->SetAntialiasing(nOriginalAA | AntialiasingFlags::EnableB2dDraw); aMtf.Play( pPrerenderVDev.get(), Point( 0, 0 ), aLogicSize ); pPrerenderVDev->SetAntialiasing(nOriginalAA); pPrerenderVDev->Pop(); SetMapMode(MapMode(MapUnit::MapPixel)); pPrerenderVDev->SetMapMode(MapMode(MapUnit::MapPixel)); maPreviewBitmap = pPrerenderVDev->GetBitmap(Point(0, 0), aVDevSize); pPrerenderVDev->SetDrawMode( nOldDrawMode ); } PrintDialog::ShowNupOrderWindow::ShowNupOrderWindow( vcl::Window* i_pParent ) : Window( i_pParent, WB_NOBORDER ) , mnOrderMode( NupOrderType::LRTB ) , mnRows( 1 ) , mnColumns( 1 ) { SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetFieldColor() ) ); } Size PrintDialog::ShowNupOrderWindow::GetOptimalSize() const { return Size(70, 70); } void PrintDialog::ShowNupOrderWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& i_rRect) { Window::Paint(rRenderContext, i_rRect); rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel)); rRenderContext.SetTextColor(rRenderContext.GetSettings().GetStyleSettings().GetFieldTextColor()); int nPages = mnRows * mnColumns; Font aFont(rRenderContext.GetSettings().GetStyleSettings().GetFieldFont()); aFont.SetFontSize(Size(0, 24)); rRenderContext.SetFont(aFont); Size aSampleTextSize(rRenderContext.GetTextWidth(OUString::number(nPages + 1)), rRenderContext.GetTextHeight()); Size aOutSize(GetOutputSizePixel()); Size aSubSize(aOutSize.Width() / mnColumns, aOutSize.Height() / mnRows); // calculate font size: shrink the sample text so it fits double fX = double(aSubSize.Width()) / double(aSampleTextSize.Width()); double fY = double(aSubSize.Height()) / double(aSampleTextSize.Height()); double fScale = (fX < fY) ? fX : fY; long nFontHeight = long(24.0 * fScale) - 3; if (nFontHeight < 5) nFontHeight = 5; aFont.SetFontSize(Size( 0, nFontHeight)); rRenderContext.SetFont(aFont); long nTextHeight = rRenderContext.GetTextHeight(); for (int i = 0; i < nPages; i++) { OUString aPageText(OUString::number(i + 1)); int nX = 0, nY = 0; switch (mnOrderMode) { case NupOrderType::LRTB: nX = (i % mnColumns); nY = (i / mnColumns); break; case NupOrderType::TBLR: nX = (i / mnRows); nY = (i % mnRows); break; case NupOrderType::RLTB: nX = mnColumns - 1 - (i % mnColumns); nY = (i / mnColumns); break; case NupOrderType::TBRL: nX = mnColumns - 1 - (i / mnRows); nY = (i % mnRows); break; } Size aTextSize(rRenderContext.GetTextWidth(aPageText), nTextHeight); int nDeltaX = (aSubSize.Width() - aTextSize.Width()) / 2; int nDeltaY = (aSubSize.Height() - aTextSize.Height()) / 2; rRenderContext.DrawText(Point(nX * aSubSize.Width() + nDeltaX, nY * aSubSize.Height() + nDeltaY), aPageText); } DecorationView aDecorationView(&rRenderContext); aDecorationView.DrawFrame(tools::Rectangle(Point(0, 0), aOutSize), DrawFrameStyle::Group); } PrintDialog::NUpTabPage::NUpTabPage( VclBuilder *pUIBuilder ) { pUIBuilder->get(mpPagesBtn, "pagespersheetbtn"); pUIBuilder->get(mpBrochureBtn, "brochure"); pUIBuilder->get(mpPagesBoxTitleTxt, "pagespersheettxt"); pUIBuilder->get(mpNupPagesBox, "paperspersheetlb"); pUIBuilder->get(mpNupNumPagesTxt, "pagestxt"); pUIBuilder->get(mpNupColEdt, "pagecols"); pUIBuilder->get(mpNupTimesTxt, "by"); pUIBuilder->get(mpNupRowsEdt, "pagerows"); pUIBuilder->get(mpPageMarginTxt1, "pagemargintxt1"); pUIBuilder->get(mpPageMarginEdt, "pagemarginsb"); pUIBuilder->get(mpPageMarginTxt2, "pagemargintxt2"); pUIBuilder->get(mpSheetMarginTxt1, "sheetmargintxt1"); pUIBuilder->get(mpSheetMarginEdt, "sheetmarginsb"); pUIBuilder->get(mpSheetMarginTxt2, "sheetmargintxt2"); pUIBuilder->get(mpNupOrientationTxt, "orientationtxt"); pUIBuilder->get(mpNupOrientationBox, "orientationlb"); pUIBuilder->get(mpNupOrderTxt, "ordertxt"); pUIBuilder->get(mpNupOrderBox, "orderlb"); pUIBuilder->get(mpNupOrderWin, "orderpreview"); pUIBuilder->get(mpBorderCB, "bordercb"); } void PrintDialog::NUpTabPage::enableNupControls( bool bEnable ) { mpNupPagesBox->Enable( bEnable ); mpNupNumPagesTxt->Enable( bEnable ); mpNupColEdt->Enable( bEnable ); mpNupTimesTxt->Enable( bEnable ); mpNupRowsEdt->Enable( bEnable ); mpPageMarginTxt1->Enable( bEnable ); mpPageMarginEdt->Enable( bEnable ); mpPageMarginTxt2->Enable( bEnable ); mpSheetMarginTxt1->Enable( bEnable ); mpSheetMarginEdt->Enable( bEnable ); mpSheetMarginTxt2->Enable( bEnable ); mpNupOrientationTxt->Enable( bEnable ); mpNupOrientationBox->Enable( bEnable ); mpNupOrderTxt->Enable( bEnable ); mpNupOrderBox->Enable( bEnable ); mpNupOrderWin->Enable( bEnable ); mpBorderCB->Enable( bEnable ); } void PrintDialog::NUpTabPage::showAdvancedControls( bool i_bShow ) { mpNupNumPagesTxt->Show( i_bShow ); mpNupColEdt->Show( i_bShow ); mpNupTimesTxt->Show( i_bShow ); mpNupRowsEdt->Show( i_bShow ); mpPageMarginTxt1->Show( i_bShow ); mpPageMarginEdt->Show( i_bShow ); mpPageMarginTxt2->Show( i_bShow ); mpSheetMarginTxt1->Show( i_bShow ); mpSheetMarginEdt->Show( i_bShow ); mpSheetMarginTxt2->Show( i_bShow ); mpNupOrientationTxt->Show( i_bShow ); mpNupOrientationBox->Show( i_bShow ); } void PrintDialog::NUpTabPage::initFromMultiPageSetup( const vcl::PrinterController::MultiPageSetup& i_rMPS ) { mpNupOrderWin->Show(); mpPagesBtn->Check(); mpBrochureBtn->Show( false ); // setup field units for metric fields const LocaleDataWrapper& rLocWrap( mpPageMarginEdt->GetLocaleDataWrapper() ); FieldUnit eUnit = FUNIT_MM; sal_uInt16 nDigits = 0; if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US ) { eUnit = FUNIT_INCH; nDigits = 2; } // set units mpPageMarginEdt->SetUnit( eUnit ); mpSheetMarginEdt->SetUnit( eUnit ); // set precision mpPageMarginEdt->SetDecimalDigits( nDigits ); mpSheetMarginEdt->SetDecimalDigits( nDigits ); mpSheetMarginEdt->SetValue( mpSheetMarginEdt->Normalize( i_rMPS.nLeftMargin ), FUNIT_100TH_MM ); mpPageMarginEdt->SetValue( mpPageMarginEdt->Normalize( i_rMPS.nHorizontalSpacing ), FUNIT_100TH_MM ); mpBorderCB->Check( i_rMPS.bDrawBorder ); mpNupRowsEdt->SetValue( i_rMPS.nRows ); mpNupColEdt->SetValue( i_rMPS.nColumns ); mpNupOrderBox->SelectEntryPos( static_cast(i_rMPS.nOrder) ); if( i_rMPS.nRows != 1 || i_rMPS.nColumns != 1 ) { mpNupPagesBox->SelectEntryPos( mpNupPagesBox->GetEntryCount()-1 ); showAdvancedControls( true ); mpNupOrderWin->setValues( i_rMPS.nOrder, i_rMPS.nColumns, i_rMPS.nRows ); } } PrintDialog::JobTabPage::JobTabPage( VclBuilder* pUIBuilder ) : maCollateBmp(SV_PRINT_COLLATE_BMP) , maNoCollateBmp(SV_PRINT_NOCOLLATE_BMP) , mnCollateUIMode(0) { pUIBuilder->get(mpPrinters, "printers"); pUIBuilder->get(mpStatusTxt, "status"); pUIBuilder->get(mpLocationTxt, "location"); pUIBuilder->get(mpCommentTxt, "comment"); pUIBuilder->get(mpSetupButton, "setup"); pUIBuilder->get(mpCopyCountField, "copycount"); pUIBuilder->get(mpCollateBox, "collate"); pUIBuilder->get(mpCollateImage, "collateimage"); pUIBuilder->get(mpReverseOrderBox, "reverseorder"); // HACK: this is not a dropdown box, but the dropdown line count // sets the results of GetOptimalSize in a normal ListBox mpPrinters->SetDropDownLineCount( 4 ); } void PrintDialog::JobTabPage::readFromSettings() { SettingsConfigItem* pItem = SettingsConfigItem::get(); OUString aValue; aValue = pItem->getValue( "PrintDialog", "CollateBox" ); if( aValue.equalsIgnoreAsciiCase("alwaysoff") ) { mnCollateUIMode = 1; mpCollateBox->Check( false ); mpCollateBox->Enable( false ); } else { mnCollateUIMode = 0; aValue = pItem->getValue( "PrintDialog", "Collate" ); mpCollateBox->Check( aValue.equalsIgnoreAsciiCase("true") ); } } void PrintDialog::JobTabPage::storeToSettings() { SettingsConfigItem* pItem = SettingsConfigItem::get(); pItem->setValue( "PrintDialog", "CopyCount", mpCopyCountField->GetText() ); pItem->setValue( "PrintDialog", "Collate", mpCollateBox->IsChecked() ? OUString("true") : OUString("false") ); } PrintDialog::OutputOptPage::OutputOptPage( VclBuilder *pUIBuilder ) { pUIBuilder->get(mpCollateSingleJobsBox, "singleprintjob"); pUIBuilder->get(mpPapersizeFromSetup, "papersizefromsetup"); } void PrintDialog::OutputOptPage::readFromSettings() { SettingsConfigItem* pItem = SettingsConfigItem::get(); OUString aValue; aValue = pItem->getValue( "PrintDialog", "CollateSingleJobs" ); if ( aValue.equalsIgnoreAsciiCase("true") ) { mpCollateSingleJobsBox->Check(); } else { mpCollateSingleJobsBox->Check( false ); } } void PrintDialog::OutputOptPage::storeToSettings() { SettingsConfigItem* pItem = SettingsConfigItem::get(); pItem->setValue( "PrintDialog", "CollateSingleJobs", mpCollateSingleJobsBox->IsChecked() ? OUString("true") : OUString("false") ); } namespace { bool lcl_ListBoxCompare( const OUString& rStr1, const OUString& rStr2 ) { return ListBox::NaturalSortCompare( rStr1, rStr2 ) < 0; } } PrintDialog::PrintDialog( vcl::Window* i_pParent, const std::shared_ptr& i_rController ) : ModalDialog(i_pParent, "PrintDialog", "vcl/ui/printdialog.ui") , mpCustomOptionsUIBuilder(nullptr) , maPController( i_rController ) , maNUpPage(m_pUIBuilder.get()) , maJobPage(m_pUIBuilder.get()) , maOptionsPage(m_pUIBuilder.get()) , maNoPageStr( VclResId( SV_PRINT_NOPAGES ) ) , mnCurPage( 0 ) , mnCachedPages( 0 ) , maPrintToFileText( VclResId( SV_PRINT_TOFILE_TXT ) ) , maDefPrtText( VclResId( SV_PRINT_DEFPRT_TXT ) ) , mbShowLayoutPage( true ) { get(mpOKButton, "ok"); get(mpCancelButton, "cancel"); get(mpHelpButton, "help"); get(mpForwardBtn, "forward"); get(mpBackwardBtn, "backward"); get(mpNumPagesText, "totalnumpages"); get(mpPageEdit, "pageedit-nospin"); get(mpTabCtrl, "tabcontrol"); get(mpPreviewWindow, "preview"); // save printbutton text, gets exchanged occasionally with print to file maPrintText = mpOKButton->GetText(); // setup preview controls mpForwardBtn->SetStyle( mpForwardBtn->GetStyle() | WB_BEVELBUTTON ); mpBackwardBtn->SetStyle( mpBackwardBtn->GetStyle() | WB_BEVELBUTTON ); maPageStr = mpNumPagesText->GetText(); // init reverse print maJobPage.mpReverseOrderBox->Check( maPController->getReversePrint() ); Printer::updatePrinters(); maJobPage.mpPrinters->InsertEntry( maPrintToFileText ); // fill printer listbox std::vector< OUString > rQueues( Printer::GetPrinterQueues() ); std::sort( rQueues.begin(), rQueues.end(), lcl_ListBoxCompare ); for( std::vector< OUString >::const_iterator it = rQueues.begin(); it != rQueues.end(); ++it ) { maJobPage.mpPrinters->InsertEntry( *it ); } // select current printer if( maJobPage.mpPrinters->GetEntryPos( maPController->getPrinter()->GetName() ) != LISTBOX_ENTRY_NOTFOUND ) { maJobPage.mpPrinters->SelectEntry( maPController->getPrinter()->GetName() ); } else { // fall back to last printer SettingsConfigItem* pItem = SettingsConfigItem::get(); OUString aValue( pItem->getValue( "PrintDialog", "LastPrinter" ) ); if( maJobPage.mpPrinters->GetEntryPos( aValue ) != LISTBOX_ENTRY_NOTFOUND ) { maJobPage.mpPrinters->SelectEntry( aValue ); maPController->setPrinter( VclPtrInstance( aValue ) ); } else { // fall back to default printer maJobPage.mpPrinters->SelectEntry( Printer::GetDefaultPrinterName() ); maPController->setPrinter( VclPtrInstance( Printer::GetDefaultPrinterName() ) ); } } // not printing to file maPController->resetPrinterOptions( false ); // get the first page preparePreview( true, true ); // update the text fields for the printer updatePrinterText(); // set a select handler maJobPage.mpPrinters->SetSelectHdl( LINK( this, PrintDialog, SelectHdl ) ); // setup sizes for N-Up Size aNupSize( maPController->getPrinter()->PixelToLogic( maPController->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ) ); if( maPController->getPrinter()->GetOrientation() == Orientation::Landscape ) { maNupLandscapeSize = aNupSize; maNupPortraitSize = Size( aNupSize.Height(), aNupSize.Width() ); } else { maNupPortraitSize = aNupSize; maNupLandscapeSize = Size( aNupSize.Height(), aNupSize.Width() ); } maNUpPage.initFromMultiPageSetup( maPController->getMultipage() ); // setup click handler on the various buttons mpOKButton->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); #if OSL_DEBUG_LEVEL > 1 mpCancelButton->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); #endif mpHelpButton->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); mpForwardBtn->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); mpBackwardBtn->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); maJobPage.mpCollateBox->SetToggleHdl( LINK( this, PrintDialog, ToggleHdl ) ); maJobPage.mpSetupButton->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); maNUpPage.mpBorderCB->SetClickHdl( LINK( this, PrintDialog, ClickHdl ) ); maOptionsPage.mpPapersizeFromSetup->SetToggleHdl( LINK( this, PrintDialog, ToggleHdl ) ); maOptionsPage.mpPapersizeFromSetup->Check( maPController->getPapersizeFromSetup() ); maJobPage.mpReverseOrderBox->SetToggleHdl( LINK( this, PrintDialog, ToggleHdl ) ); maOptionsPage.mpCollateSingleJobsBox->SetToggleHdl( LINK( this, PrintDialog, ToggleHdl ) ); maNUpPage.mpPagesBtn->SetToggleHdl( LINK( this, PrintDialog, ToggleRadioHdl ) ); // setup modify hdl mpPageEdit->SetModifyHdl( LINK( this, PrintDialog, ModifyHdl ) ); maJobPage.mpCopyCountField->SetModifyHdl( LINK( this, PrintDialog, ModifyHdl ) ); maNUpPage.mpNupRowsEdt->SetModifyHdl( LINK( this, PrintDialog, ModifyHdl ) ); maNUpPage.mpNupColEdt->SetModifyHdl( LINK( this, PrintDialog, ModifyHdl ) ); maNUpPage.mpPageMarginEdt->SetModifyHdl( LINK( this, PrintDialog, ModifyHdl ) ); maNUpPage.mpSheetMarginEdt->SetModifyHdl( LINK( this, PrintDialog, ModifyHdl ) ); // setup select hdl maNUpPage.mpNupPagesBox->SetSelectHdl( LINK( this, PrintDialog, SelectHdl ) ); maNUpPage.mpNupOrientationBox->SetSelectHdl( LINK( this, PrintDialog, SelectHdl ) ); maNUpPage.mpNupOrderBox->SetSelectHdl( LINK( this, PrintDialog, SelectHdl ) ); // setup optional UI options set by application setupOptionalUI(); // remove layout page if unwanted if (!mbShowLayoutPage) mpTabCtrl->RemovePage(mpTabCtrl->GetPageId(2)); // restore settings from last run readFromSettings(); // setup dependencies checkControlDependencies(); if ( maPController->getBoolProperty( "HideHelpButton", false ) ) mpHelpButton->Hide(); // set initial focus to "Number of copies" maJobPage.mpCopyCountField->GrabFocus(); maJobPage.mpCopyCountField->SetSelection( Selection(0, 0xFFFF) ); updateNupFromPages(); } PrintDialog::~PrintDialog() { disposeOnce(); } void PrintDialog::dispose() { mpCustomOptionsUIBuilder.reset(); mpTabCtrl.clear(); mpPreviewWindow.clear(); mpPageEdit.clear(); mpNumPagesText.clear(); mpBackwardBtn.clear(); mpForwardBtn.clear(); mpOKButton.clear(); mpCancelButton.clear(); mpHelpButton.clear(); maPController.reset(); maControlToPropertyMap.clear(); maControlToNumValMap.clear(); ModalDialog::dispose(); } void PrintDialog::readFromSettings() { maJobPage.readFromSettings(); maOptionsPage.readFromSettings(); // read last selected tab page; if it exists, activate it SettingsConfigItem* pItem = SettingsConfigItem::get(); OUString aValue = pItem->getValue( "PrintDialog", "LastPage" ); sal_uInt16 nCount = mpTabCtrl->GetPageCount(); for( sal_uInt16 i = 0; i < nCount; i++ ) { sal_uInt16 nPageId = mpTabCtrl->GetPageId( i ); if( aValue == mpTabCtrl->GetPageText( nPageId ) ) { mpTabCtrl->SelectTabPage( nPageId ); break; } } // persistent window state OUString aWinState( pItem->getValue( "PrintDialog", "WindowState" ) ); if( !aWinState.isEmpty() ) SetWindowState( OUStringToOString( aWinState, RTL_TEXTENCODING_UTF8 ) ); } void PrintDialog::storeToSettings() { maJobPage.storeToSettings(); maOptionsPage.storeToSettings(); // store last selected printer SettingsConfigItem* pItem = SettingsConfigItem::get(); pItem->setValue( "PrintDialog", "ToFile", isPrintToFile() ? OUString("true") : OUString("false") ); pItem->setValue( "PrintDialog", "LastPrinter", isPrintToFile() ? Printer::GetDefaultPrinterName() : maJobPage.mpPrinters->GetSelectedEntry() ); pItem->setValue( "PrintDialog", "LastPage", mpTabCtrl->GetPageText( mpTabCtrl->GetCurPageId() ) ); pItem->setValue( "PrintDialog", "WindowState", OStringToOUString( GetWindowState(), RTL_TEXTENCODING_UTF8 ) ); pItem->Commit(); } bool PrintDialog::isPrintToFile() { return ( maJobPage.mpPrinters->GetSelectedEntryPos() == 0 ); } bool PrintDialog::isCollate() { return maJobPage.mpCopyCountField->GetValue() > 1 && maJobPage.mpCollateBox->IsChecked(); } bool PrintDialog::isSingleJobs() { return maOptionsPage.mpCollateSingleJobsBox->IsChecked(); } void setHelpId( vcl::Window* i_pWindow, const Sequence< OUString >& i_rHelpIds, sal_Int32 i_nIndex ) { if( i_nIndex >= 0 && i_nIndex < i_rHelpIds.getLength() ) i_pWindow->SetHelpId( OUStringToOString( i_rHelpIds.getConstArray()[i_nIndex], RTL_TEXTENCODING_UTF8 ) ); } static void setHelpText( vcl::Window* i_pWindow, const Sequence< OUString >& i_rHelpTexts, sal_Int32 i_nIndex ) { // without a help text set and the correct smartID, // help texts will be retrieved from the online help system if( i_nIndex >= 0 && i_nIndex < i_rHelpTexts.getLength() ) i_pWindow->SetHelpText( i_rHelpTexts.getConstArray()[i_nIndex] ); } void PrintDialog::setupOptionalUI() { const Sequence< PropertyValue >& rOptions( maPController->getUIOptions() ); for( int i = 0; i < rOptions.getLength(); i++ ) { if (rOptions[i].Name == "OptionsUIFile") { OUString sOptionsUIFile; rOptions[i].Value >>= sOptionsUIFile; vcl::Window *pCustom = get("customcontents"); mpCustomOptionsUIBuilder.reset(new VclBuilder(pCustom, getUIRootDir(), sOptionsUIFile)); vcl::Window *pWindow = mpCustomOptionsUIBuilder->get_widget_root(); pWindow->Show(); continue; } Sequence< beans::PropertyValue > aOptProp; rOptions[i].Value >>= aOptProp; // extract ui element OUString aCtrlType; OString aID; OUString aText; OUString aPropertyName; Sequence< OUString > aChoices; Sequence< sal_Bool > aChoicesDisabled; Sequence< OUString > aHelpTexts; Sequence< OUString > aIDs; Sequence< OUString > aHelpIds; sal_Int64 nMinValue = 0, nMaxValue = 0; OUString aGroupingHint; OUString aDependsOnName; sal_Int32 nDependsOnValue = 0; bool bUseDependencyRow = false; for( int n = 0; n < aOptProp.getLength(); n++ ) { const beans::PropertyValue& rEntry( aOptProp[ n ] ); if ( rEntry.Name == "ID" ) { rEntry.Value >>= aIDs; aID = OUStringToOString(aIDs[0], RTL_TEXTENCODING_UTF8); } if ( rEntry.Name == "Text" ) { rEntry.Value >>= aText; } else if ( rEntry.Name == "ControlType" ) { rEntry.Value >>= aCtrlType; } else if ( rEntry.Name == "Choices" ) { rEntry.Value >>= aChoices; } else if ( rEntry.Name == "ChoicesDisabled" ) { rEntry.Value >>= aChoicesDisabled; } else if ( rEntry.Name == "Property" ) { PropertyValue aVal; rEntry.Value >>= aVal; aPropertyName = aVal.Name; } else if ( rEntry.Name == "Enabled" ) { bool bValue = true; rEntry.Value >>= bValue; } else if ( rEntry.Name == "GroupingHint" ) { rEntry.Value >>= aGroupingHint; } else if ( rEntry.Name == "DependsOnName" ) { rEntry.Value >>= aDependsOnName; } else if ( rEntry.Name == "DependsOnEntry" ) { rEntry.Value >>= nDependsOnValue; } else if ( rEntry.Name == "AttachToDependency" ) { rEntry.Value >>= bUseDependencyRow; } else if ( rEntry.Name == "MinValue" ) { rEntry.Value >>= nMinValue; } else if ( rEntry.Name == "MaxValue" ) { rEntry.Value >>= nMaxValue; } else if ( rEntry.Name == "HelpText" ) { if( ! (rEntry.Value >>= aHelpTexts) ) { OUString aHelpText; if( rEntry.Value >>= aHelpText ) { aHelpTexts.realloc( 1 ); *aHelpTexts.getArray() = aHelpText; } } } else if ( rEntry.Name == "HelpId" ) { if( ! (rEntry.Value >>= aHelpIds ) ) { OUString aHelpId; if( rEntry.Value >>= aHelpId ) { aHelpIds.realloc( 1 ); *aHelpIds.getArray() = aHelpId; } } } else if ( rEntry.Name == "HintNoLayoutPage" ) { bool bNoLayoutPage = false; rEntry.Value >>= bNoLayoutPage; mbShowLayoutPage = ! bNoLayoutPage; } } if (aCtrlType == "Group" && !aID.isEmpty()) { TabPage *pPage = get(aID); if (!pPage && mpCustomOptionsUIBuilder) pPage = mpCustomOptionsUIBuilder->get(aID); if (!pPage) continue; sal_uInt16 nPageId = mpTabCtrl->GetPageId(*pPage); mpTabCtrl->SetPageText(nPageId, aText); // set help id if (aHelpIds.getLength() > 0) mpTabCtrl->SetHelpId(nPageId, OUStringToOString(aHelpIds.getConstArray()[0], RTL_TEXTENCODING_UTF8)); // set help text if (aHelpTexts.getLength() > 0) mpTabCtrl->SetHelpText(nPageId, aHelpTexts.getConstArray()[0]); pPage->Show(); } else if (aCtrlType == "Subgroup" && !aID.isEmpty()) { vcl::Window *pFrame = get(aID); if (!pFrame && mpCustomOptionsUIBuilder) pFrame = mpCustomOptionsUIBuilder->get(aID); if (!pFrame) continue; pFrame->SetText(aText); // set help id setHelpId(pFrame, aHelpIds, 0); // set help text setHelpText(pFrame, aHelpTexts, 0); pFrame->Show(); } // EVIL else if( aCtrlType == "Bool" && aGroupingHint == "LayoutPage" && aPropertyName == "PrintProspect" ) { maNUpPage.mpBrochureBtn->SetText( aText ); maNUpPage.mpBrochureBtn->Show(); bool bVal = false; PropertyValue* pVal = maPController->getValue( aPropertyName ); if( pVal ) pVal->Value >>= bVal; maNUpPage.mpBrochureBtn->Check( bVal ); maNUpPage.mpBrochureBtn->Enable( maPController->isUIOptionEnabled( aPropertyName ) && pVal != nullptr ); maNUpPage.mpBrochureBtn->SetToggleHdl( LINK( this, PrintDialog, ToggleRadioHdl ) ); maPropertyToWindowMap[ aPropertyName ].emplace_back(maNUpPage.mpBrochureBtn ); maControlToPropertyMap[maNUpPage.mpBrochureBtn] = aPropertyName; // set help id setHelpId( maNUpPage.mpBrochureBtn, aHelpIds, 0 ); // set help text setHelpText( maNUpPage.mpBrochureBtn, aHelpTexts, 0 ); } else if (aCtrlType == "Bool") { // add a check box CheckBox* pNewBox = get(aID); if (!pNewBox && mpCustomOptionsUIBuilder) pNewBox = mpCustomOptionsUIBuilder->get(aID); if (!pNewBox) continue; pNewBox->SetText( aText ); pNewBox->Show(); bool bVal = false; PropertyValue* pVal = maPController->getValue( aPropertyName ); if( pVal ) pVal->Value >>= bVal; pNewBox->Check( bVal ); pNewBox->SetToggleHdl( LINK( this, PrintDialog, UIOption_CheckHdl ) ); maPropertyToWindowMap[ aPropertyName ].emplace_back(pNewBox ); maControlToPropertyMap[pNewBox] = aPropertyName; // set help id setHelpId( pNewBox, aHelpIds, 0 ); // set help text setHelpText( pNewBox, aHelpTexts, 0 ); } else if (aCtrlType == "Radio") { sal_Int32 nCurHelpText = 0; // iterate options sal_Int32 nSelectVal = 0; PropertyValue* pVal = maPController->getValue( aPropertyName ); if( pVal && pVal->Value.hasValue() ) pVal->Value >>= nSelectVal; for( sal_Int32 m = 0; m < aChoices.getLength(); m++ ) { aID = OUStringToOString(aIDs[m], RTL_TEXTENCODING_UTF8); RadioButton* pBtn = get(aID); if (!pBtn && mpCustomOptionsUIBuilder) pBtn = mpCustomOptionsUIBuilder->get(aID); if (!pBtn) continue; pBtn->SetText( aChoices[m] ); pBtn->Check( m == nSelectVal ); pBtn->SetToggleHdl( LINK( this, PrintDialog, UIOption_RadioHdl ) ); if( aChoicesDisabled.getLength() > m && aChoicesDisabled[m] ) pBtn->Enable( false ); pBtn->Show(); maPropertyToWindowMap[ aPropertyName ].emplace_back(pBtn ); maControlToPropertyMap[pBtn] = aPropertyName; maControlToNumValMap[pBtn] = m; // set help id setHelpId( pBtn, aHelpIds, nCurHelpText ); // set help text setHelpText( pBtn, aHelpTexts, nCurHelpText ); nCurHelpText++; } } else if ( aCtrlType == "List" ) { ListBox* pList = get(aID); if (!pList && mpCustomOptionsUIBuilder) pList = mpCustomOptionsUIBuilder->get(aID); if (!pList) continue; // iterate options for( sal_Int32 m = 0; m < aChoices.getLength(); m++ ) { pList->InsertEntry( aChoices[m] ); } sal_Int32 nSelectVal = 0; PropertyValue* pVal = maPController->getValue( aPropertyName ); if( pVal && pVal->Value.hasValue() ) pVal->Value >>= nSelectVal; pList->SelectEntryPos( static_cast(nSelectVal) ); pList->SetSelectHdl( LINK( this, PrintDialog, UIOption_SelectHdl ) ); pList->SetDropDownLineCount( static_cast(aChoices.getLength()) ); pList->Show(); // set help id setHelpId( pList, aHelpIds, 0 ); // set help text setHelpText( pList, aHelpTexts, 0 ); maPropertyToWindowMap[ aPropertyName ].emplace_back(pList ); maControlToPropertyMap[pList] = aPropertyName; } else if ( aCtrlType == "Range" ) { NumericField* pField = get(aID); if (!pField && mpCustomOptionsUIBuilder) pField = mpCustomOptionsUIBuilder->get(aID); if (!pField) continue; // set min/max and current value if( nMinValue != nMaxValue ) { pField->SetMin( nMinValue ); pField->SetMax( nMaxValue ); } sal_Int64 nCurVal = 0; PropertyValue* pVal = maPController->getValue( aPropertyName ); if( pVal && pVal->Value.hasValue() ) pVal->Value >>= nCurVal; pField->SetValue( nCurVal ); pField->SetModifyHdl( LINK( this, PrintDialog, UIOption_ModifyHdl ) ); pField->Show(); // set help id setHelpId( pField, aHelpIds, 0 ); // set help text setHelpText( pField, aHelpTexts, 0 ); maPropertyToWindowMap[ aPropertyName ].emplace_back(pField ); maControlToPropertyMap[pField] = aPropertyName; } else if (aCtrlType == "Edit") { Edit *pField = get(aID); if (!pField && mpCustomOptionsUIBuilder) pField = mpCustomOptionsUIBuilder->get(aID); if (!pField) continue; OUString aCurVal; PropertyValue* pVal = maPController->getValue( aPropertyName ); if( pVal && pVal->Value.hasValue() ) pVal->Value >>= aCurVal; pField->SetText( aCurVal ); pField->SetModifyHdl( LINK( this, PrintDialog, UIOption_ModifyHdl ) ); pField->Show(); // set help id setHelpId( pField, aHelpIds, 0 ); // set help text setHelpText( pField, aHelpTexts, 0 ); maPropertyToWindowMap[ aPropertyName ].emplace_back(pField ); maControlToPropertyMap[pField] = aPropertyName; } else { SAL_WARN( "vcl", "Unsupported UI option: \"" << aCtrlType << '"'); } } // #i106506# if no brochure button, then the singular Pages radio button // makes no sense, so replace it by a FixedText label if (!maNUpPage.mpBrochureBtn->IsVisible() && maNUpPage.mpPagesBtn->IsVisible()) { maNUpPage.mpPagesBoxTitleTxt->SetText( maNUpPage.mpPagesBtn->GetText() ); maNUpPage.mpPagesBoxTitleTxt->Show(); maNUpPage.mpPagesBtn->Show( false ); } // update enable states checkOptionalControlDependencies(); vcl::Window *pPageRange = get("pagerange"); // print range not shown (currently math only) -> hide spacer line and reverse order if (!pPageRange || !pPageRange->IsVisible()) { maJobPage.mpReverseOrderBox->Show( false ); } if (!mpCustomOptionsUIBuilder) mpTabCtrl->RemovePage(mpTabCtrl->GetPageId(1)); } void PrintDialog::DataChanged( const DataChangedEvent& i_rDCEvt ) { // react on settings changed if( i_rDCEvt.GetType() == DataChangedEventType::SETTINGS ) checkControlDependencies(); ModalDialog::DataChanged( i_rDCEvt ); } void PrintDialog::checkControlDependencies() { if( maJobPage.mpCopyCountField->GetValue() > 1 ) maJobPage.mpCollateBox->Enable( maJobPage.mnCollateUIMode == 0 ); else maJobPage.mpCollateBox->Enable( false ); Image aImg(maJobPage.mpCollateBox->IsChecked() ? maJobPage.maCollateBmp : maJobPage.maNoCollateBmp); Size aImgSize( aImg.GetSizePixel() ); // adjust size of image maJobPage.mpCollateImage->SetSizePixel( aImgSize ); maJobPage.mpCollateImage->SetImage( aImg ); // enable setup button only for printers that can be setup bool bHaveSetup = maPController->getPrinter()->HasSupport( PrinterSupport::SetupDialog ); maJobPage.mpSetupButton->Enable(bHaveSetup); } void PrintDialog::checkOptionalControlDependencies() { for( auto it = maControlToPropertyMap.begin(); it != maControlToPropertyMap.end(); ++it ) { bool bShouldbeEnabled = maPController->isUIOptionEnabled( it->second ); if( ! bShouldbeEnabled ) { // enable controls that are directly attached to a dependency anyway // if the normally disabled controls get modified, change the dependency // so the control would be enabled // example: in print range "Print All" is selected, "Page Range" is then of course // not selected and the Edit for the Page Range would be disabled // as a convenience we should enable the Edit anyway and automatically select // "Page Range" instead of "Print All" if the Edit gets modified if( maReverseDependencySet.find( it->second ) != maReverseDependencySet.end() ) { OUString aDep( maPController->getDependency( it->second ) ); // if the dependency is at least enabled, then enable this control anyway if( !aDep.isEmpty() && maPController->isUIOptionEnabled( aDep ) ) bShouldbeEnabled = true; } } if( bShouldbeEnabled && dynamic_cast(it->first.get()) ) { auto r_it = maControlToNumValMap.find( it->first ); if( r_it != maControlToNumValMap.end() ) { bShouldbeEnabled = maPController->isUIChoiceEnabled( it->second, r_it->second ); } } bool bIsEnabled = it->first->IsEnabled(); // Enable does not do a change check first, so can be less cheap than expected if( bShouldbeEnabled != bIsEnabled ) it->first->Enable( bShouldbeEnabled ); } } static OUString searchAndReplace( const OUString& i_rOrig, const char* i_pRepl, sal_Int32 i_nReplLen, const OUString& i_rRepl ) { sal_Int32 nPos = i_rOrig.indexOfAsciiL( i_pRepl, i_nReplLen ); if( nPos != -1 ) { OUStringBuffer aBuf( i_rOrig.getLength() ); aBuf.append( i_rOrig.getStr(), nPos ); aBuf.append( i_rRepl ); if( nPos + i_nReplLen < i_rOrig.getLength() ) aBuf.append( i_rOrig.getStr() + nPos + i_nReplLen ); return aBuf.makeStringAndClear(); } return i_rOrig; } void PrintDialog::updatePrinterText() { const OUString aDefPrt( Printer::GetDefaultPrinterName() ); const QueueInfo* pInfo = Printer::GetQueueInfo( maJobPage.mpPrinters->GetSelectedEntry(), true ); if( pInfo ) { maJobPage.mpLocationTxt->SetText( pInfo->GetLocation() ); maJobPage.mpCommentTxt->SetText( pInfo->GetComment() ); // FIXME: status text OUString aStatus; if( aDefPrt == pInfo->GetPrinterName() ) aStatus = maDefPrtText; maJobPage.mpStatusTxt->SetText( aStatus ); } else { maJobPage.mpLocationTxt->SetText( OUString() ); maJobPage.mpCommentTxt->SetText( OUString() ); maJobPage.mpStatusTxt->SetText( OUString() ); } } void PrintDialog::setPreviewText() { OUString aNewText( searchAndReplace( maPageStr, "%n", 2, OUString::number( mnCachedPages ) ) ); mpNumPagesText->SetText( aNewText ); } void PrintDialog::preparePreview( bool i_bNewPage, bool i_bMayUseCache ) { // page range may have changed depending on options sal_Int32 nPages = maPController->getFilteredPageCount(); mnCachedPages = nPages; if( mnCurPage >= nPages ) mnCurPage = nPages-1; if( mnCurPage < 0 ) mnCurPage = 0; setPreviewText(); mpPageEdit->SetMin( 1 ); mpPageEdit->SetMax( nPages ); if( i_bNewPage ) { const MapMode aMapMode( MapUnit::Map100thMM ); GDIMetaFile aMtf; VclPtr aPrt( maPController->getPrinter() ); if( nPages > 0 ) { PrinterController::PageSize aPageSize = maPController->getFilteredPageFile( mnCurPage, aMtf, i_bMayUseCache ); if( ! aPageSize.bFullPaper ) { Point aOff( aPrt->PixelToLogic( aPrt->GetPageOffsetPixel(), aMapMode ) ); aMtf.Move( aOff.X(), aOff.Y() ); } } Size aCurPageSize = aPrt->PixelToLogic( aPrt->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ); mpPreviewWindow->setPreview( aMtf, aCurPageSize, aPrt->GetPaperName(), nPages > 0 ? OUString() : maNoPageStr, aPrt->GetDPIX(), aPrt->GetDPIY(), aPrt->GetPrinterOptions().IsConvertToGreyscales() ); mpForwardBtn->Enable( mnCurPage < nPages-1 ); mpBackwardBtn->Enable( mnCurPage != 0 ); mpPageEdit->Enable( nPages > 1 ); } } Size const & PrintDialog::getJobPageSize() { if( maFirstPageSize.Width() == 0 && maFirstPageSize.Height() == 0) { maFirstPageSize = maNupPortraitSize; GDIMetaFile aMtf; if( maPController->getPageCountProtected() > 0 ) { PrinterController::PageSize aPageSize = maPController->getPageFile( 0, aMtf, true ); maFirstPageSize = aPageSize.aSize; } } return maFirstPageSize; } void PrintDialog::updateNupFromPages() { sal_IntPtr nPages = sal_IntPtr(maNUpPage.mpNupPagesBox->GetSelectedEntryData()); int nRows = int(maNUpPage.mpNupRowsEdt->GetValue()); int nCols = int(maNUpPage.mpNupColEdt->GetValue()); long nPageMargin = maNUpPage.mpPageMarginEdt->Denormalize(maNUpPage.mpPageMarginEdt->GetValue( FUNIT_100TH_MM )); long nSheetMargin = maNUpPage.mpSheetMarginEdt->Denormalize(maNUpPage.mpSheetMarginEdt->GetValue( FUNIT_100TH_MM )); bool bCustom = false; if( nPages == 1 ) { nRows = nCols = 1; nSheetMargin = 0; nPageMargin = 0; } else if( nPages == 2 || nPages == 4 || nPages == 6 || nPages == 9 || nPages == 16 ) { Size aJobPageSize( getJobPageSize() ); bool bPortrait = aJobPageSize.Width() < aJobPageSize.Height(); if( nPages == 2 ) { if( bPortrait ) { nRows = 1; nCols = 2; } else { nRows = 2; nCols = 1; } } else if( nPages == 4 ) nRows = nCols = 2; else if( nPages == 6 ) { if( bPortrait ) { nRows = 2; nCols = 3; } else { nRows = 3; nCols = 2; } } else if( nPages == 9 ) nRows = nCols = 3; else if( nPages == 16 ) nRows = nCols = 4; nPageMargin = 0; nSheetMargin = 0; } else bCustom = true; if( nPages > 1 ) { // set upper limits for margins based on job page size and rows/columns Size aSize( getJobPageSize() ); // maximum sheet distance: 1/2 sheet long nHorzMax = aSize.Width()/2; long nVertMax = aSize.Height()/2; if( nSheetMargin > nHorzMax ) nSheetMargin = nHorzMax; if( nSheetMargin > nVertMax ) nSheetMargin = nVertMax; maNUpPage.mpSheetMarginEdt->SetMax( maNUpPage.mpSheetMarginEdt->Normalize( std::min(nHorzMax, nVertMax) ), FUNIT_100TH_MM ); // maximum page distance nHorzMax = (aSize.Width() - 2*nSheetMargin); if( nCols > 1 ) nHorzMax /= (nCols-1); nVertMax = (aSize.Height() - 2*nSheetMargin); if( nRows > 1 ) nHorzMax /= (nRows-1); if( nPageMargin > nHorzMax ) nPageMargin = nHorzMax; if( nPageMargin > nVertMax ) nPageMargin = nVertMax; maNUpPage.mpPageMarginEdt->SetMax( maNUpPage.mpSheetMarginEdt->Normalize( std::min(nHorzMax, nVertMax ) ), FUNIT_100TH_MM ); } maNUpPage.mpNupRowsEdt->SetValue( nRows ); maNUpPage.mpNupColEdt->SetValue( nCols ); maNUpPage.mpPageMarginEdt->SetValue( maNUpPage.mpPageMarginEdt->Normalize( nPageMargin ), FUNIT_100TH_MM ); maNUpPage.mpSheetMarginEdt->SetValue( maNUpPage.mpSheetMarginEdt->Normalize( nSheetMargin ), FUNIT_100TH_MM ); maNUpPage.showAdvancedControls( bCustom ); updateNup(); } #define SV_PRINT_PRT_NUP_ORIENTATION_PORTRAIT 1 #define SV_PRINT_PRT_NUP_ORIENTATION_LANDSCAPE 2 void PrintDialog::updateNup() { int nRows = int(maNUpPage.mpNupRowsEdt->GetValue()); int nCols = int(maNUpPage.mpNupColEdt->GetValue()); long nPageMargin = maNUpPage.mpPageMarginEdt->Denormalize(maNUpPage.mpPageMarginEdt->GetValue( FUNIT_100TH_MM )); long nSheetMargin = maNUpPage.mpSheetMarginEdt->Denormalize(maNUpPage.mpSheetMarginEdt->GetValue( FUNIT_100TH_MM )); PrinterController::MultiPageSetup aMPS; aMPS.nRows = nRows; aMPS.nColumns = nCols; aMPS.nLeftMargin = aMPS.nTopMargin = aMPS.nRightMargin = aMPS.nBottomMargin = nSheetMargin; aMPS.nHorizontalSpacing = aMPS.nVerticalSpacing = nPageMargin; aMPS.bDrawBorder = maNUpPage.mpBorderCB->IsChecked(); aMPS.nOrder = static_cast(maNUpPage.mpNupOrderBox->GetSelectedEntryPos()); int nOrientationMode = maNUpPage.mpNupOrientationBox->GetSelectedEntryPos(); if( nOrientationMode == SV_PRINT_PRT_NUP_ORIENTATION_LANDSCAPE ) aMPS.aPaperSize = maNupLandscapeSize; else if( nOrientationMode == SV_PRINT_PRT_NUP_ORIENTATION_PORTRAIT ) aMPS.aPaperSize = maNupPortraitSize; else // automatic mode { // get size of first real page to see if it is portrait or landscape // we assume same page sizes for all the pages for this Size aPageSize = getJobPageSize(); Size aMultiSize( aPageSize.Width() * nCols, aPageSize.Height() * nRows ); if( aMultiSize.Width() > aMultiSize.Height() ) // fits better on landscape aMPS.aPaperSize = maNupLandscapeSize; else aMPS.aPaperSize = maNupPortraitSize; } maPController->setMultipage( aMPS ); maNUpPage.mpNupOrderWin->setValues( aMPS.nOrder, nCols, nRows ); preparePreview( true, true ); } IMPL_LINK( PrintDialog, SelectHdl, ListBox&, rBox, void ) { if( &rBox == maJobPage.mpPrinters ) { if ( rBox.GetSelectedEntryPos() != 0) { OUString aNewPrinter( rBox.GetSelectedEntry() ); // set new printer maPController->setPrinter( VclPtrInstance( aNewPrinter ) ); maPController->resetPrinterOptions( false ); // update text fields mpOKButton->SetText( maPrintText ); updatePrinterText(); preparePreview(); } else // print to file { // use the default printer or FIXME: the last used one? maPController->setPrinter( VclPtrInstance( Printer::GetDefaultPrinterName() ) ); mpOKButton->SetText( maPrintToFileText ); maPController->resetPrinterOptions( true ); preparePreview( true, true ); } } else if( &rBox == maNUpPage.mpNupOrientationBox || &rBox == maNUpPage.mpNupOrderBox ) { updateNup(); } else if( &rBox == maNUpPage.mpNupPagesBox ) { if( !maNUpPage.mpPagesBtn->IsChecked() ) maNUpPage.mpPagesBtn->Check(); updateNupFromPages(); } } IMPL_LINK( PrintDialog, ToggleRadioHdl, RadioButton&, rButton, void ) { ClickHdl(static_cast(&rButton)); } IMPL_LINK( PrintDialog, ToggleHdl, CheckBox&, rButton, void ) { ClickHdl(&rButton); } IMPL_LINK( PrintDialog, ClickHdl, Button*, pButton, void ) { if( pButton == mpOKButton || pButton == mpCancelButton ) { storeToSettings(); EndDialog( pButton == mpOKButton ? RET_OK : RET_CANCEL ); } else if( pButton == mpHelpButton ) { // start help system Help* pHelp = Application::GetHelp(); if( pHelp ) { pHelp->Start( "vcl/ui/printdialog", mpOKButton ); } } else if( pButton == mpForwardBtn ) { previewForward(); } else if( pButton == mpBackwardBtn ) { previewBackward(); } else if( pButton == maOptionsPage.mpPapersizeFromSetup ) { bool bChecked = maOptionsPage.mpPapersizeFromSetup->IsChecked(); maPController->setPapersizeFromSetup( bChecked ); maPController->setValue( "PapersizeFromSetup", makeAny( bChecked ) ); preparePreview( true, true ); } else if( pButton == maNUpPage.mpBrochureBtn ) { PropertyValue* pVal = getValueForWindow( pButton ); if( pVal ) { bool bVal = maNUpPage.mpBrochureBtn->IsChecked(); pVal->Value <<= bVal; checkOptionalControlDependencies(); // update preview and page settings preparePreview(); } if( maNUpPage.mpBrochureBtn->IsChecked() ) { maNUpPage.mpNupPagesBox->SelectEntryPos( 0 ); updateNupFromPages(); maNUpPage.showAdvancedControls( false ); maNUpPage.enableNupControls( false ); } } else if( pButton == maNUpPage.mpPagesBtn ) { maNUpPage.enableNupControls( true ); updateNupFromPages(); } else if( pButton == maJobPage.mpCollateBox ) { maPController->setValue( "Collate", makeAny( isCollate() ) ); checkControlDependencies(); } else if( pButton == maJobPage.mpReverseOrderBox ) { bool bChecked = maJobPage.mpReverseOrderBox->IsChecked(); maPController->setReversePrint( bChecked ); maPController->setValue( "PrintReverse", makeAny( bChecked ) ); preparePreview( true, true ); } else if( pButton == maNUpPage.mpBorderCB ) { updateNup(); } else { if( pButton == maJobPage.mpSetupButton ) { maPController->setupPrinter(GetFrameWeld()); // tdf#63905 don't use cache: page size may change preparePreview(); } checkControlDependencies(); } } IMPL_LINK( PrintDialog, ModifyHdl, Edit&, rEdit, void ) { checkControlDependencies(); if( &rEdit == maNUpPage.mpNupRowsEdt || &rEdit == maNUpPage.mpNupColEdt || &rEdit == maNUpPage.mpSheetMarginEdt || &rEdit == maNUpPage.mpPageMarginEdt ) { updateNupFromPages(); } else if( &rEdit == mpPageEdit ) { mnCurPage = sal_Int32( mpPageEdit->GetValue() - 1 ); preparePreview( true, true ); } else if( &rEdit == maJobPage.mpCopyCountField ) { maPController->setValue( "CopyCount", makeAny( sal_Int32(maJobPage.mpCopyCountField->GetValue()) ) ); maPController->setValue( "Collate", makeAny( isCollate() ) ); } } PropertyValue* PrintDialog::getValueForWindow( vcl::Window* i_pWindow ) const { PropertyValue* pVal = nullptr; auto it = maControlToPropertyMap.find( i_pWindow ); if( it != maControlToPropertyMap.end() ) { pVal = maPController->getValue( it->second ); SAL_WARN_IF( !pVal, "vcl", "property value not found" ); } else { OSL_FAIL( "changed control not in property map" ); } return pVal; } void PrintDialog::updateWindowFromProperty( const OUString& i_rProperty ) { beans::PropertyValue* pValue = maPController->getValue( i_rProperty ); auto it = maPropertyToWindowMap.find( i_rProperty ); if( pValue && it != maPropertyToWindowMap.end() ) { const std::vector< VclPtr >& rWindows( it->second ); if( ! rWindows.empty() ) { bool bVal = false; sal_Int32 nVal = -1; if( pValue->Value >>= bVal ) { // we should have a CheckBox for this one CheckBox* pBox = dynamic_cast< CheckBox* >( rWindows.front().get() ); if( pBox ) { pBox->Check( bVal ); } else if ( i_rProperty == "PrintProspect" ) { // EVIL special case if( bVal ) maNUpPage.mpBrochureBtn->Check(); else maNUpPage.mpPagesBtn->Check(); } else { SAL_WARN( "vcl", "missing a checkbox" ); } } else if( pValue->Value >>= nVal ) { // this could be a ListBox or a RadioButtonGroup ListBox* pList = dynamic_cast< ListBox* >( rWindows.front().get() ); if( pList ) { pList->SelectEntryPos( static_cast< sal_uInt16 >(nVal) ); } else if( nVal >= 0 && nVal < sal_Int32(rWindows.size() ) ) { RadioButton* pBtn = dynamic_cast< RadioButton* >( rWindows[nVal].get() ); SAL_WARN_IF( !pBtn, "vcl", "unexpected control for property" ); if( pBtn ) pBtn->Check(); } } } } } void PrintDialog::makeEnabled( vcl::Window* i_pWindow ) { auto it = maControlToPropertyMap.find( i_pWindow ); if( it != maControlToPropertyMap.end() ) { OUString aDependency( maPController->makeEnabled( it->second ) ); if( !aDependency.isEmpty() ) updateWindowFromProperty( aDependency ); } } IMPL_LINK( PrintDialog, UIOption_CheckHdl, CheckBox&, i_rBox, void ) { PropertyValue* pVal = getValueForWindow( &i_rBox ); if( pVal ) { makeEnabled( &i_rBox ); bool bVal = i_rBox.IsChecked(); pVal->Value <<= bVal; checkOptionalControlDependencies(); // update preview and page settings preparePreview(); } } IMPL_LINK( PrintDialog, UIOption_RadioHdl, RadioButton&, i_rBtn, void ) { // this handler gets called for all radiobuttons that get unchecked, too // however we only want one notification for the new value (that is for // the button that gets checked) if( i_rBtn.IsChecked() ) { PropertyValue* pVal = getValueForWindow( &i_rBtn ); auto it = maControlToNumValMap.find( &i_rBtn ); if( pVal && it != maControlToNumValMap.end() ) { makeEnabled( &i_rBtn ); sal_Int32 nVal = it->second; pVal->Value <<= nVal; // tdf#63905 use paper size set in printer properties if (pVal->Name == "PageOptions") maPController->resetPaperToLastConfigured(); checkOptionalControlDependencies(); // update preview and page settings preparePreview(); } } } IMPL_LINK( PrintDialog, UIOption_SelectHdl, ListBox&, i_rBox, void ) { PropertyValue* pVal = getValueForWindow( &i_rBox ); if( pVal ) { makeEnabled( &i_rBox ); sal_Int32 nVal( i_rBox.GetSelectedEntryPos() ); pVal->Value <<= nVal; //If we are in impress we start in print slides mode and get a //maFirstPageSize for slides which are usually landscape mode, if we //change to notes which are usually in portrait mode, and then visit //n-up print, we will assume notes are in landscape unless we throw //away maFirstPageSize when we change page content type if (pVal->Name == "PageContentType") maFirstPageSize = Size(); checkOptionalControlDependencies(); // update preview and page settings preparePreview(); } } IMPL_LINK( PrintDialog, UIOption_ModifyHdl, Edit&, i_rBox, void ) { PropertyValue* pVal = getValueForWindow( &i_rBox ); if( pVal ) { makeEnabled( &i_rBox ); NumericField* pNum = dynamic_cast(&i_rBox); MetricField* pMetric = dynamic_cast(&i_rBox); if( pNum ) { sal_Int64 nVal = pNum->GetValue(); pVal->Value <<= nVal; } else if( pMetric ) { sal_Int64 nVal = pMetric->GetValue(); pVal->Value <<= nVal; } else { OUString aVal( i_rBox.GetText() ); pVal->Value <<= aVal; } checkOptionalControlDependencies(); // update preview and page settings preparePreview(); } } void PrintDialog::Command( const CommandEvent& rEvt ) { if( rEvt.GetCommand() == CommandEventId::Wheel ) { const CommandWheelData* pWheelData = rEvt.GetWheelData(); if( pWheelData->GetDelta() > 0 ) previewForward(); else if( pWheelData->GetDelta() < 0 ) previewBackward(); } } void PrintDialog::Resize() { // maLayout.setManagedArea( Rectangle( Point( 0, 0 ), GetSizePixel() ) ); // and do the preview; however the metafile does not need to be gotten anew preparePreview( false ); Dialog::Resize(); } void PrintDialog::previewForward() { mpPageEdit->Up(); } void PrintDialog::previewBackward() { mpPageEdit->Down(); } // PrintProgressDialog PrintProgressDialog::PrintProgressDialog(vcl::Window* i_pParent, int i_nMax) : ModelessDialog(i_pParent, "PrintProgressDialog", "vcl/ui/printprogressdialog.ui") , mbCanceled(false) , mnCur(0) , mnMax(i_nMax) { get(mpButton, "cancel"); get(mpProgress, "progressbar"); get(mpText, "label"); if( mnMax < 1 ) mnMax = 1; maStr = mpText->GetText(); //just multiply largest value by 10 and take the width of that string as //the max size we will want OUString aNewText( searchAndReplace( maStr, "%p", 2, OUString::number( mnMax * 10 ) ) ); aNewText = searchAndReplace( aNewText, "%n", 2, OUString::number( mnMax * 10 ) ); mpText->SetText( aNewText ); mpText->set_width_request(mpText->get_preferred_size().Width()); //Pick a useful max width mpProgress->set_width_request(mpProgress->LogicToPixel(Size(100, 0), MapMode(MapUnit::MapAppFont)).Width()); mpButton->SetClickHdl( LINK( this, PrintProgressDialog, ClickHdl ) ); } PrintProgressDialog::~PrintProgressDialog() { disposeOnce(); } void PrintProgressDialog::dispose() { mpText.clear(); mpProgress.clear(); mpButton.clear(); ModelessDialog::dispose(); } IMPL_LINK( PrintProgressDialog, ClickHdl, Button*, pButton, void ) { if( pButton == mpButton ) mbCanceled = true; } void PrintProgressDialog::setProgress( int i_nCurrent ) { mnCur = i_nCurrent; if( mnMax < 1 ) mnMax = 1; mpProgress->SetValue(mnCur*100/mnMax); OUString aNewText( searchAndReplace( maStr, "%p", 2, OUString::number( mnCur ) ) ); aNewText = searchAndReplace( aNewText, "%n", 2, OUString::number( mnMax ) ); mpText->SetText( aNewText ); } void PrintProgressDialog::tick() { if( mnCur < mnMax ) setProgress( ++mnCur ); } void PrintProgressDialog::reset() { mbCanceled = false; setProgress( 0 ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */