xref: /aoo42x/main/vcl/source/gdi/print3.cxx (revision 0dccdc5d)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "precompiled_vcl.hxx"
29 
30 #include "vcl/print.hxx"
31 #include "vcl/svapp.hxx"
32 #include "vcl/metaact.hxx"
33 #include "vcl/msgbox.hxx"
34 #include "vcl/configsettings.hxx"
35 
36 #include "printdlg.hxx"
37 #include "svdata.hxx"
38 #include "salinst.hxx"
39 #include "salprn.hxx"
40 #include "svids.hrc"
41 
42 #include "tools/urlobj.hxx"
43 
44 #include "com/sun/star/ui/dialogs/XFilePicker.hpp"
45 #include "com/sun/star/ui/dialogs/XFilterManager.hpp"
46 #include "com/sun/star/ui/dialogs/TemplateDescription.hpp"
47 #include "com/sun/star/ui/dialogs/ExecutableDialogResults.hpp"
48 #include "com/sun/star/view/DuplexMode.hpp"
49 #include "com/sun/star/lang/XMultiServiceFactory.hpp"
50 #include "com/sun/star/awt/Size.hpp"
51 #include "comphelper/processfactory.hxx"
52 
53 #include <hash_map>
54 #include <hash_set>
55 
56 using namespace com::sun::star;
57 using namespace com::sun::star::uno;
58 using namespace com::sun::star::beans;
59 using namespace vcl;
60 
61 class ImplPageCache
62 {
63     struct CacheEntry
64     {
65         GDIMetaFile                 aPage;
66         PrinterController::PageSize aSize;
67     };
68 
69     std::vector< CacheEntry >  maPages;
70     std::vector< sal_Int32 >    maPageNumbers;
71     std::vector< sal_Int32 >    maCacheRanking;
72 
73     static const sal_Int32 nCacheSize = 6;
74 
75     void updateRanking( sal_Int32 nLastHit )
76     {
77         if( maCacheRanking[0] != nLastHit )
78         {
79             bool bMove = false;
80             for( sal_Int32 i = nCacheSize-1; i > 0; i-- )
81             {
82                 if( maCacheRanking[i] == nLastHit )
83                     bMove = true;
84                 maCacheRanking[i] = maCacheRanking[i-1];
85             }
86             maCacheRanking[0] = nLastHit;
87         }
88     }
89 
90 public:
91     ImplPageCache()
92     : maPages( nCacheSize )
93     , maPageNumbers( nCacheSize, -1 )
94     , maCacheRanking( nCacheSize )
95     {
96         for( sal_Int32 i = 0; i < nCacheSize; i++ )
97             maCacheRanking[i] = nCacheSize - i - 1;
98     }
99 
100     // caution: does not ensure uniqueness
101     void insert( sal_Int32 i_nPageNo, const GDIMetaFile& i_rPage, const PrinterController::PageSize& i_rSize )
102     {
103         sal_Int32 nReplacePage = maCacheRanking.back();
104         maPages[ nReplacePage ].aPage = i_rPage;
105         maPages[ nReplacePage ].aSize = i_rSize;
106         maPageNumbers[ nReplacePage ] = i_nPageNo;
107         // cache insertion means in our case, the page was just queried
108         // so update the ranking
109         updateRanking( nReplacePage );
110     }
111 
112     // caution: bad algorithm; should there ever be reason to increase the cache size beyond 6
113     // this needs to be urgently rewritten. However do NOT increase the cache size lightly,
114     // whole pages can be rather memory intensive
115     bool get( sal_Int32 i_nPageNo, GDIMetaFile& o_rPageFile, PrinterController::PageSize& o_rSize )
116     {
117         for( sal_Int32 i = 0; i < nCacheSize; ++i )
118         {
119             if( maPageNumbers[i] == i_nPageNo )
120             {
121                 updateRanking( i );
122                 o_rPageFile = maPages[i].aPage;
123                 o_rSize = maPages[i].aSize;
124                 return true;
125             }
126         }
127         return false;
128     }
129 
130     void invalidate()
131     {
132         for( sal_Int32 i = 0; i < nCacheSize; ++i )
133         {
134             maPageNumbers[i] = -1;
135             maPages[i].aPage.Clear();
136             maCacheRanking[i] = nCacheSize - i - 1;
137         }
138     }
139 };
140 
141 class vcl::ImplPrinterControllerData
142 {
143 public:
144     struct ControlDependency
145     {
146         rtl::OUString       maDependsOnName;
147         sal_Int32           mnDependsOnEntry;
148 
149         ControlDependency() : mnDependsOnEntry( -1 ) {}
150     };
151 
152     typedef std::hash_map< rtl::OUString, size_t, rtl::OUStringHash > PropertyToIndexMap;
153     typedef std::hash_map< rtl::OUString, ControlDependency, rtl::OUStringHash > ControlDependencyMap;
154     typedef std::hash_map< rtl::OUString, Sequence< sal_Bool >, rtl::OUStringHash > ChoiceDisableMap;
155 
156     boost::shared_ptr<Printer>                                  mpPrinter;
157     Sequence< PropertyValue >                                   maUIOptions;
158     std::vector< PropertyValue >                                maUIProperties;
159     std::vector< bool >                                         maUIPropertyEnabled;
160     PropertyToIndexMap                                          maPropertyToIndex;
161     Link                                                        maOptionChangeHdl;
162     ControlDependencyMap                                        maControlDependencies;
163     ChoiceDisableMap                                            maChoiceDisableMap;
164     sal_Bool                                                    mbFirstPage;
165     sal_Bool                                                    mbLastPage;
166     sal_Bool                                                    mbReversePageOrder;
167     view::PrintableState                                        meJobState;
168 
169     vcl::PrinterController::MultiPageSetup                      maMultiPage;
170 
171     vcl::PrintProgressDialog*                                   mpProgress;
172 
173     ImplPageCache                                               maPageCache;
174 
175     // set by user through printer config dialog
176     // if set, pages are centered and trimmed onto the fixed page
177     Size                                                        maFixedPageSize;
178     sal_Int32                                                   mnDefaultPaperBin;
179     sal_Int32                                                   mnFixedPaperBin;
180 
181     ImplPrinterControllerData() :
182         mbFirstPage( sal_True ),
183         mbLastPage( sal_False ),
184         mbReversePageOrder( sal_False ),
185         meJobState( view::PrintableState_JOB_STARTED ),
186         mpProgress( NULL ),
187         mnDefaultPaperBin( -1 ),
188         mnFixedPaperBin( -1 )
189     {}
190     ~ImplPrinterControllerData() { delete mpProgress; }
191 
192     Size getRealPaperSize( const Size& i_rPageSize, bool bNoNUP ) const
193     {
194         if( maFixedPageSize.Width() > 0 && maFixedPageSize.Height() > 0 )
195             return maFixedPageSize;
196         if( maMultiPage.nRows * maMultiPage.nColumns > 1 && ! bNoNUP )
197             return maMultiPage.aPaperSize;
198         return i_rPageSize;
199     }
200     bool isFixedPageSize() const
201     { return maFixedPageSize.Width() != 0 && maFixedPageSize.Height() != 0; }
202     PrinterController::PageSize modifyJobSetup( const Sequence< PropertyValue >& i_rProps, bool bNoNUP );
203 };
204 
205 PrinterController::PrinterController()
206     : mpImplData( new ImplPrinterControllerData )
207 {
208 }
209 
210 PrinterController::PrinterController( const boost::shared_ptr<Printer>& i_pPrinter )
211     : mpImplData( new ImplPrinterControllerData )
212 {
213     mpImplData->mpPrinter = i_pPrinter;
214 }
215 
216 static rtl::OUString queryFile( Printer* pPrinter )
217 {
218     rtl::OUString aResult;
219 
220     uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
221     if( xFactory.is() )
222     {
223         uno::Sequence< uno::Any > aTempl( 1 );
224         aTempl.getArray()[0] <<= ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION;
225         uno::Reference< ui::dialogs::XFilePicker > xFilePicker(
226             xFactory->createInstanceWithArguments(
227                 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.dialogs.FilePicker" ) ),
228                 aTempl ), uno::UNO_QUERY );
229         DBG_ASSERT( xFilePicker.is(), "could not get FilePicker service" );
230 
231         uno::Reference< ui::dialogs::XFilterManager > xFilterMgr( xFilePicker, uno::UNO_QUERY );
232         if( xFilePicker.is() && xFilterMgr.is() )
233         {
234             try
235             {
236 #ifdef UNX
237                 // add PostScript and PDF
238                 bool bPS = true, bPDF = true;
239                 if( pPrinter )
240                 {
241                     if( pPrinter->GetCapabilities( PRINTER_CAPABILITIES_PDF ) )
242                         bPS = false;
243                     else
244                         bPDF = false;
245                 }
246                 if( bPS )
247                     xFilterMgr->appendFilter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PostScript" ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.ps" ) ) );
248                 if( bPDF )
249                     xFilterMgr->appendFilter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Portable Document Format" ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.pdf" ) ) );
250 #elif defined WNT
251 		(void)pPrinter;
252                 xFilterMgr->appendFilter( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.PRN" ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.prn" ) ) );
253 #endif
254                 // add arbitrary files
255                 xFilterMgr->appendFilter( String( VclResId( SV_STDTEXT_ALLFILETYPES ) ), ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "*.*" ) ) );
256             }
257             catch( lang::IllegalArgumentException rExc )
258             {
259                 DBG_ERRORFILE( "caught IllegalArgumentException when registering filter\n" );
260             }
261 
262             if( xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK )
263             {
264                 uno::Sequence< ::rtl::OUString > aPathSeq( xFilePicker->getFiles() );
265 				INetURLObject aObj( aPathSeq[0] );
266 				aResult = aObj.PathToFileName();
267             }
268         }
269     }
270     return aResult;
271 }
272 
273 struct PrintJobAsync
274 {
275     boost::shared_ptr<PrinterController>  mpController;
276     JobSetup                            maInitSetup;
277 
278     PrintJobAsync( const boost::shared_ptr<PrinterController>& i_pController,
279                    const JobSetup& i_rInitSetup
280                    )
281     : mpController( i_pController ), maInitSetup( i_rInitSetup )
282     {}
283 
284     DECL_LINK( ExecJob, void* );
285 };
286 
287 IMPL_LINK( PrintJobAsync, ExecJob, void*, EMPTYARG )
288 {
289     Printer::ImplPrintJob( mpController, maInitSetup );
290 
291     // clean up, do not access members after this
292     delete this;
293 
294     return 0;
295 }
296 
297 void Printer::PrintJob( const boost::shared_ptr<PrinterController>& i_pController,
298                         const JobSetup& i_rInitSetup
299                         )
300 {
301     sal_Bool bSynchronous = sal_False;
302     beans::PropertyValue* pVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Wait" ) ) );
303     if( pVal )
304         pVal->Value >>= bSynchronous;
305 
306     if( bSynchronous )
307         ImplPrintJob( i_pController, i_rInitSetup );
308     else
309     {
310         PrintJobAsync* pAsync = new PrintJobAsync( i_pController, i_rInitSetup );
311         Application::PostUserEvent( LINK( pAsync, PrintJobAsync, ExecJob ) );
312     }
313 }
314 
315 void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pController,
316                             const JobSetup& i_rInitSetup
317                             )
318 {
319     boost::shared_ptr<PrinterController> pController( i_pController );
320 
321     // check if there is a default printer; if not, show an error box (if appropriate)
322     if( GetDefaultPrinterName().Len() == 0  )
323     {
324         if(  pController->isShowDialogs()
325              // && ! pController->isDirectPrint()
326            )
327         {
328             ErrorBox aBox( NULL, VclResId( SV_PRINT_NOPRINTERWARNING ) );
329             aBox.Execute();
330         }
331         pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDirect" ) ),
332                                makeAny( sal_False ) );
333     }
334 
335     // setup printer
336 
337     // #i114306# changed behavior back from persistence
338     // if no specific printer is already set, create the default printer
339     if( ! pController->getPrinter() )
340     {
341         rtl::OUString aPrinterName( i_rInitSetup.GetPrinterName() );
342         boost::shared_ptr<Printer> pPrinter( new Printer( aPrinterName ) );
343         pPrinter->SetJobSetup( i_rInitSetup );
344         pController->setPrinter( pPrinter );
345     }
346 
347     // reset last page property
348     i_pController->setLastPage( sal_False );
349 
350     // update "PageRange" property inferring from other properties:
351     // case 1: "Pages" set from UNO API ->
352     //         setup "Print Selection" and insert "PageRange" attribute
353     // case 2: "All pages" is selected
354     //         update "Page range" attribute to have a sensible default,
355     //         but leave "All" as selected
356 
357     // "Pages" attribute from API is now equivalent to "PageRange"
358     // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
359     // Argh ! That sure needs cleaning up
360     beans::PropertyValue* pContentVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintRange" ) ) );
361     if( ! pContentVal )
362         pContentVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintContent" ) ) );
363 
364     // case 1: UNO API has set "Pages"
365     beans::PropertyValue* pPagesVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Pages" ) ) );
366     if( pPagesVal )
367     {
368         rtl::OUString aPagesVal;
369         pPagesVal->Value >>= aPagesVal;
370         if( aPagesVal.getLength() )
371         {
372             // "Pages" attribute from API is now equivalent to "PageRange"
373             // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
374             // Argh ! That sure needs cleaning up
375             if( pContentVal )
376             {
377                 pContentVal->Value = makeAny( sal_Int32( 1 ) );
378                 i_pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageRange" ) ), pPagesVal->Value );
379             }
380         }
381     }
382     // case 2: is "All" selected ?
383     else if( pContentVal )
384     {
385         sal_Int32 nContent = -1;
386         if( pContentVal->Value >>= nContent )
387         {
388             if( nContent == 0 )
389             {
390                 // do not overwrite PageRange if it is already set
391                 beans::PropertyValue* pRangeVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageRange" ) ) );
392                 rtl::OUString aRange;
393                 if( pRangeVal )
394                     pRangeVal->Value >>= aRange;
395                 if( aRange.getLength() == 0 )
396                 {
397                     sal_Int32 nPages = i_pController->getPageCount();
398                     if( nPages > 0 )
399                     {
400                         rtl::OUStringBuffer aBuf( 32 );
401                         aBuf.appendAscii( "1" );
402                         if( nPages > 1 )
403                         {
404                             aBuf.appendAscii( "-" );
405                             aBuf.append( nPages );
406                         }
407                         i_pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PageRange" ) ), makeAny( aBuf.makeStringAndClear() ) );
408                     }
409                 }
410             }
411         }
412     }
413 
414     beans::PropertyValue* pReverseVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintReverse" ) ) );
415     if( pReverseVal )
416     {
417         sal_Bool bReverse = sal_False;
418         pReverseVal->Value >>= bReverse;
419         pController->setReversePrint( bReverse );
420     }
421 
422     // setup NUp printing from properties
423     sal_Int32 nRows = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpRows" ) ), 1 );
424     sal_Int32 nCols = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpColumns" ) ), 1 );
425     if( nRows > 1 || nCols > 1 )
426     {
427         PrinterController::MultiPageSetup aMPS;
428         aMPS.nRows         = nRows > 1 ? nRows : 1;
429         aMPS.nColumns      = nCols > 1 ? nCols : 1;
430         sal_Int32 nValue = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpPageMarginLeft" ) ), aMPS.nLeftMargin );
431         if( nValue >= 0 )
432             aMPS.nLeftMargin = nValue;
433         nValue = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpPageMarginRight" ) ), aMPS.nRightMargin );
434         if( nValue >= 0 )
435             aMPS.nRightMargin = nValue;
436         nValue = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpPageMarginTop" ) ), aMPS.nTopMargin );
437         if( nValue >= 0 )
438             aMPS.nTopMargin = nValue;
439         nValue = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpPageMarginBottom" ) ), aMPS.nBottomMargin );
440         if( nValue >= 0 )
441             aMPS.nBottomMargin = nValue;
442         nValue = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpHorizontalSpacing" ) ), aMPS.nHorizontalSpacing );
443         if( nValue >= 0 )
444             aMPS.nHorizontalSpacing = nValue;
445         nValue = i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpVerticalSpacing" ) ), aMPS.nVerticalSpacing );
446         if( nValue >= 0 )
447             aMPS.nVerticalSpacing = nValue;
448         aMPS.bDrawBorder = i_pController->getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpDrawBorder" ) ), aMPS.bDrawBorder );
449         aMPS.nOrder = static_cast<PrinterController::NupOrderType>(i_pController->getIntProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpSubPageOrder" ) ), aMPS.nOrder ));
450         aMPS.aPaperSize = i_pController->getPrinter()->PixelToLogic( i_pController->getPrinter()->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) );
451         beans::PropertyValue* pPgSizeVal = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "NUpPaperSize" ) ) );
452         awt::Size aSizeVal;
453         if( pPgSizeVal && (pPgSizeVal->Value >>= aSizeVal) )
454         {
455             aMPS.aPaperSize.Width() = aSizeVal.Width;
456             aMPS.aPaperSize.Height() = aSizeVal.Height;
457         }
458 
459         i_pController->setMultipage( aMPS );
460     }
461 
462     // in direct print case check whether there is anything to print.
463     // if not, show an errorbox (if appropriate)
464     if( pController->isShowDialogs() && pController->isDirectPrint() )
465     {
466         if( pController->getFilteredPageCount() == 0 )
467         {
468             ErrorBox aBox( NULL, VclResId( SV_PRINT_NOCONTENT ) );
469             aBox.Execute();
470             return;
471         }
472     }
473 
474     // check if the printer brings up its own dialog
475     // in that case leave the work to that dialog
476     if( ! pController->getPrinter()->GetCapabilities( PRINTER_CAPABILITIES_EXTERNALDIALOG ) &&
477         ! pController->isDirectPrint() &&
478         pController->isShowDialogs()
479         )
480     {
481         try
482         {
483             PrintDialog aDlg( NULL, i_pController );
484             if( ! aDlg.Execute() )
485             {
486                 GDIMetaFile aPageFile;
487                 i_pController->abortJob();
488                 return;
489             }
490             if( aDlg.isPrintToFile() )
491             {
492                 rtl::OUString aFile = queryFile( pController->getPrinter().get() );
493                 if( ! aFile.getLength() )
494                 {
495                     i_pController->abortJob();
496                     return;
497                 }
498                 pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LocalFileName" ) ),
499                                        makeAny( aFile ) );
500             }
501             else if( aDlg.isSingleJobs() )
502             {
503                 pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ),
504                                        makeAny( sal_True ) );
505             }
506         }
507         catch( std::bad_alloc& )
508         {
509         }
510     }
511 
512     pController->pushPropertiesToPrinter();
513 
514     rtl::OUString aJobName;
515     beans::PropertyValue* pJobNameVal = pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "JobName" ) ) );
516     if( pJobNameVal )
517         pJobNameVal->Value >>= aJobName;
518 
519     pController->getPrinter()->StartJob( String( aJobName ), pController );
520 
521     pController->jobFinished( pController->getJobState() );
522 }
523 
524 bool Printer::StartJob( const rtl::OUString& i_rJobName, boost::shared_ptr<vcl::PrinterController>& i_pController )
525 {
526 	mnError = PRINTER_OK;
527 
528 	if ( IsDisplayPrinter() )
529 		return sal_False;
530 
531 	if ( IsJobActive() || IsPrinting() )
532 		return sal_False;
533 
534 	sal_uLong   nCopies = mnCopyCount;
535 	bool    bCollateCopy = mbCollateCopy;
536 	bool    bUserCopy = sal_False;
537 
538     if ( nCopies > 1 )
539     {
540         sal_uLong nDevCopy;
541 
542         if ( bCollateCopy )
543             nDevCopy = GetCapabilities( PRINTER_CAPABILITIES_COLLATECOPIES );
544         else
545             nDevCopy = GetCapabilities( PRINTER_CAPABILITIES_COPIES );
546 
547         // need to do copies by hand ?
548         if ( nCopies > nDevCopy )
549         {
550             bUserCopy = sal_True;
551             nCopies = 1;
552             bCollateCopy = sal_False;
553         }
554     }
555     else
556         bCollateCopy = sal_False;
557 
558 
559     ImplSVData* pSVData = ImplGetSVData();
560     mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
561 
562     if ( !mpPrinter )
563         return sal_False;
564 
565     sal_Bool bSinglePrintJobs = sal_False;
566     beans::PropertyValue* pSingleValue = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) );
567     if( pSingleValue )
568     {
569         pSingleValue->Value >>= bSinglePrintJobs;
570     }
571 
572     beans::PropertyValue* pFileValue = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LocalFileName" ) ) );
573     if( pFileValue )
574     {
575         rtl::OUString aFile;
576         pFileValue->Value >>= aFile;
577         if( aFile.getLength() )
578         {
579             mbPrintFile = sal_True;
580             maPrintFile = aFile;
581             bSinglePrintJobs = sal_False;
582         }
583     }
584 
585     XubString* pPrintFile = NULL;
586     if ( mbPrintFile )
587         pPrintFile = &maPrintFile;
588     mpPrinterOptions->ReadFromConfig( mbPrintFile );
589 
590     maJobName		        = i_rJobName;
591     mnCurPage		        = 1;
592     mnCurPrintPage	        = 1;
593     mbPrinting		        = sal_True;
594     if( GetCapabilities( PRINTER_CAPABILITIES_USEPULLMODEL ) )
595     {
596         mbJobActive             = sal_True;
597         // sallayer does all necessary page printing
598         // and also handles showing a dialog
599         // that also means it must call jobStarted when the dialog is finished
600         // it also must set the JobState of the Controller
601         if( mpPrinter->StartJob( pPrintFile,
602                                  i_rJobName,
603                                  Application::GetDisplayName(),
604                                  maJobSetup.ImplGetConstData(),
605                                  *i_pController ) )
606         {
607             EndJob();
608         }
609         else
610         {
611             mnError = ImplSalPrinterErrorCodeToVCL( mpPrinter->GetErrorCode() );
612             if ( !mnError )
613                 mnError = PRINTER_GENERALERROR;
614             pSVData->mpDefInst->DestroyPrinter( mpPrinter );
615             mnCurPage		    = 0;
616             mnCurPrintPage	    = 0;
617             mbPrinting		    = sal_False;
618             mpPrinter = NULL;
619 
620             return false;
621         }
622     }
623     else
624     {
625         // possibly a dialog has been shown
626         // now the real job starts
627         i_pController->setJobState( view::PrintableState_JOB_STARTED );
628         i_pController->jobStarted();
629 
630         int nJobs = 1;
631         int nOuterRepeatCount = 1;
632         int nInnerRepeatCount = 1;
633         if( bUserCopy )
634         {
635             if( mbCollateCopy )
636                 nOuterRepeatCount = mnCopyCount;
637             else
638                 nInnerRepeatCount = mnCopyCount;
639         }
640         if( bSinglePrintJobs )
641         {
642             nJobs = mnCopyCount;
643             nCopies = 1;
644             nOuterRepeatCount = nInnerRepeatCount = 1;
645         }
646 
647         for( int nJobIteration = 0; nJobIteration < nJobs; nJobIteration++ )
648         {
649             bool bError = false, bAborted = false;
650             if( mpPrinter->StartJob( pPrintFile,
651                                      i_rJobName,
652                                      Application::GetDisplayName(),
653                                      nCopies,
654                                      bCollateCopy,
655                                      i_pController->isDirectPrint(),
656                                      maJobSetup.ImplGetConstData() ) )
657             {
658                 mbJobActive             = sal_True;
659                 i_pController->createProgressDialog();
660                 int nPages = i_pController->getFilteredPageCount();
661                 for( int nOuterIteration = 0; nOuterIteration < nOuterRepeatCount && ! bAborted; nOuterIteration++ )
662                 {
663                     for( int nPage = 0; nPage < nPages && ! bAborted; nPage++ )
664                     {
665                         for( int nInnerIteration = 0; nInnerIteration < nInnerRepeatCount && ! bAborted; nInnerIteration++ )
666                         {
667                             if( nPage == nPages-1 &&
668                                 nOuterIteration == nOuterRepeatCount-1 &&
669                                 nInnerIteration == nInnerRepeatCount-1 &&
670                                 nJobIteration == nJobs-1 )
671                             {
672                                 i_pController->setLastPage( sal_True );
673                             }
674                             i_pController->printFilteredPage( nPage );
675                             if( i_pController->isProgressCanceled() )
676                             {
677                                 i_pController->abortJob();
678                                 bAborted = true;
679                             }
680                         }
681                     }
682                     // FIXME: duplex ?
683                 }
684                 EndJob();
685 
686                 if( nJobIteration < nJobs-1 )
687                 {
688                     mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
689 
690                     if ( mpPrinter )
691                     {
692                         maJobName		        = i_rJobName;
693                         mnCurPage		        = 1;
694                         mnCurPrintPage	        = 1;
695                         mbPrinting		        = sal_True;
696                     }
697                     else
698                         bError = true;
699                 }
700             }
701             else
702                 bError = true;
703 
704             if( bError )
705             {
706                 mnError = ImplSalPrinterErrorCodeToVCL( mpPrinter->GetErrorCode() );
707                 if ( !mnError )
708                     mnError = PRINTER_GENERALERROR;
709                 i_pController->setJobState( mnError == PRINTER_ABORT
710                                             ? view::PrintableState_JOB_ABORTED
711                                             : view::PrintableState_JOB_FAILED );
712                 if( mpPrinter )
713                     pSVData->mpDefInst->DestroyPrinter( mpPrinter );
714                 mnCurPage		    = 0;
715                 mnCurPrintPage	    = 0;
716                 mbPrinting		    = sal_False;
717                 mpPrinter = NULL;
718 
719                 return false;
720             }
721         }
722 
723         if( i_pController->getJobState() == view::PrintableState_JOB_STARTED )
724             i_pController->setJobState( view::PrintableState_JOB_SPOOLED );
725     }
726 
727     // make last used printer persistent for UI jobs
728     if( i_pController->isShowDialogs() && ! i_pController->isDirectPrint() )
729     {
730         SettingsConfigItem* pItem = SettingsConfigItem::get();
731         pItem->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintDialog" ) ),
732                          rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LastPrinterUsed" ) ),
733                          GetName()
734                          );
735     }
736 
737 	return true;
738 }
739 
740 PrinterController::~PrinterController()
741 {
742     delete mpImplData;
743 }
744 
745 view::PrintableState PrinterController::getJobState() const
746 {
747     return mpImplData->meJobState;
748 }
749 
750 void PrinterController::setJobState( view::PrintableState i_eState )
751 {
752     mpImplData->meJobState = i_eState;
753 }
754 
755 const boost::shared_ptr<Printer>& PrinterController::getPrinter() const
756 {
757     return mpImplData->mpPrinter;
758 }
759 
760 void PrinterController::setPrinter( const boost::shared_ptr<Printer>& i_rPrinter )
761 {
762     mpImplData->mpPrinter = i_rPrinter;
763     setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Name" ) ),
764               makeAny( rtl::OUString( i_rPrinter->GetName() ) ) );
765     mpImplData->mnDefaultPaperBin = mpImplData->mpPrinter->GetPaperBin();
766     mpImplData->mnFixedPaperBin = -1;
767 }
768 
769 void PrinterController:: resetPrinterOptions( bool i_bFileOutput )
770 {
771     PrinterOptions aOpt;
772     aOpt.ReadFromConfig( i_bFileOutput );
773     mpImplData->mpPrinter->SetPrinterOptions( aOpt );
774 }
775 
776 bool PrinterController::setupPrinter( Window* i_pParent )
777 {
778     bool bRet = false;
779     if( mpImplData->mpPrinter.get() )
780     {
781         // get old data
782         Size aPaperSize( mpImplData->mpPrinter->PixelToLogic(
783             mpImplData->mpPrinter->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) );
784         sal_uInt16 nPaperBin = mpImplData->mpPrinter->GetPaperBin();
785 
786         // call driver setup
787         bRet = mpImplData->mpPrinter->Setup( i_pParent );
788         if( bRet )
789         {
790             // was papersize or bin  overridden ? if so we need to take action
791             Size aNewPaperSize( mpImplData->mpPrinter->PixelToLogic(
792                 mpImplData->mpPrinter->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) );
793             sal_uInt16 nNewPaperBin = mpImplData->mpPrinter->GetPaperBin();
794             if( aNewPaperSize != aPaperSize || nNewPaperBin != nPaperBin )
795             {
796                 mpImplData->maFixedPageSize = aNewPaperSize;
797                 mpImplData->maPageCache.invalidate();
798                 awt::Size aOverrideSize;
799                 aOverrideSize.Width = aNewPaperSize.Width();
800                 aOverrideSize.Height = aNewPaperSize.Height();
801                 setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OverridePageSize" ) ),
802                           makeAny( aOverrideSize ) );
803                 mpImplData->mnFixedPaperBin = nNewPaperBin;
804             }
805         }
806     }
807     return bRet;
808 }
809 
810 PrinterController::PageSize vcl::ImplPrinterControllerData::modifyJobSetup( const Sequence< PropertyValue >& i_rProps, bool bNoNUP )
811 {
812     PrinterController::PageSize aPageSize;
813     aPageSize.aSize = mpPrinter->GetPaperSize();
814     awt::Size aSetSize, aIsSize;
815     sal_Int32 nPaperBin = mnDefaultPaperBin;
816     for( sal_Int32 nProperty = 0, nPropertyCount = i_rProps.getLength(); nProperty < nPropertyCount; ++nProperty )
817     {
818         if( i_rProps[ nProperty ].Name.equalsAscii( "PreferredPageSize" ) )
819         {
820             i_rProps[ nProperty ].Value >>= aSetSize;
821         }
822         else if( i_rProps[ nProperty ].Name.equalsAscii( "PageSize" ) )
823         {
824             i_rProps[ nProperty ].Value >>= aIsSize;
825         }
826         else if( i_rProps[ nProperty ].Name.equalsAscii( "PageIncludesNonprintableArea" ) )
827         {
828             sal_Bool bVal = sal_False;
829             i_rProps[ nProperty ].Value >>= bVal;
830             aPageSize.bFullPaper = static_cast<bool>(bVal);
831         }
832         else if( i_rProps[ nProperty ].Name.equalsAscii( "PrinterPaperTray" ) )
833         {
834             sal_Int32 nBin = -1;
835             i_rProps[ nProperty ].Value >>= nBin;
836             if( nBin >= 0 && nBin < mpPrinter->GetPaperBinCount() )
837                 nPaperBin = nBin;
838         }
839     }
840 
841     Size aCurSize( mpPrinter->GetPaperSize() );
842     if( aSetSize.Width && aSetSize.Height )
843     {
844         Size aSetPaperSize( aSetSize.Width, aSetSize.Height );
845         Size aRealPaperSize( getRealPaperSize( aSetPaperSize, bNoNUP ) );
846         if( aRealPaperSize != aCurSize )
847             aIsSize = aSetSize;
848     }
849 
850     if( aIsSize.Width && aIsSize.Height )
851     {
852         aPageSize.aSize.Width() = aIsSize.Width;
853         aPageSize.aSize.Height() = aIsSize.Height;
854 
855         Size aRealPaperSize( getRealPaperSize( aPageSize.aSize, bNoNUP ) );
856         if( aRealPaperSize != aCurSize )
857             mpPrinter->SetPaperSizeUser( aRealPaperSize, ! isFixedPageSize() );
858     }
859 
860     if( nPaperBin != -1 && nPaperBin != mpPrinter->GetPaperBin() )
861         mpPrinter->SetPaperBin( nPaperBin );
862 
863     return aPageSize;
864 }
865 
866 int PrinterController::getPageCountProtected() const
867 {
868     const MapMode aMapMode( MAP_100TH_MM );
869 
870     mpImplData->mpPrinter->Push();
871     mpImplData->mpPrinter->SetMapMode( aMapMode );
872     int nPages = getPageCount();
873     mpImplData->mpPrinter->Pop();
874     return nPages;
875 }
876 
877 Sequence< beans::PropertyValue > PrinterController::getPageParametersProtected( int i_nPage ) const
878 {
879     const MapMode aMapMode( MAP_100TH_MM );
880 
881     mpImplData->mpPrinter->Push();
882     mpImplData->mpPrinter->SetMapMode( aMapMode );
883     Sequence< beans::PropertyValue > aResult( getPageParameters( i_nPage ) );
884     mpImplData->mpPrinter->Pop();
885     return aResult;
886 }
887 
888 PrinterController::PageSize PrinterController::getPageFile( int i_nUnfilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
889 {
890     // update progress if necessary
891     if( mpImplData->mpProgress )
892     {
893         // do nothing if printing is canceled
894         if( mpImplData->mpProgress->isCanceled() )
895             return PrinterController::PageSize();
896         mpImplData->mpProgress->tick();
897         Application::Reschedule( true );
898     }
899 
900     if( i_bMayUseCache )
901     {
902         PrinterController::PageSize aPageSize;
903         if( mpImplData->maPageCache.get( i_nUnfilteredPage, o_rMtf, aPageSize ) )
904         {
905             return aPageSize;
906         }
907     }
908     else
909         mpImplData->maPageCache.invalidate();
910 
911     o_rMtf.Clear();
912 
913     // get page parameters
914     Sequence< PropertyValue > aPageParm( getPageParametersProtected( i_nUnfilteredPage ) );
915     const MapMode aMapMode( MAP_100TH_MM );
916 
917     mpImplData->mpPrinter->Push();
918     mpImplData->mpPrinter->SetMapMode( aMapMode );
919 
920     // modify job setup if necessary
921     PrinterController::PageSize aPageSize = mpImplData->modifyJobSetup( aPageParm, true );
922 
923     o_rMtf.SetPrefSize( aPageSize.aSize );
924     o_rMtf.SetPrefMapMode( aMapMode );
925 
926     mpImplData->mpPrinter->EnableOutput( sal_False );
927 
928     o_rMtf.Record( mpImplData->mpPrinter.get() );
929 
930     printPage( i_nUnfilteredPage );
931 
932     o_rMtf.Stop();
933     o_rMtf.WindStart();
934     mpImplData->mpPrinter->Pop();
935 
936     if( i_bMayUseCache )
937         mpImplData->maPageCache.insert( i_nUnfilteredPage, o_rMtf, aPageSize );
938 
939     // reset "FirstPage" property to false now we've gotten at least our first one
940     mpImplData->mbFirstPage = sal_False;
941 
942     return aPageSize;
943 }
944 
945 static void appendSubPage( GDIMetaFile& o_rMtf, const Rectangle& i_rClipRect, GDIMetaFile& io_rSubPage, bool i_bDrawBorder )
946 {
947     // intersect all clipregion actions with our clip rect
948     io_rSubPage.WindStart();
949     io_rSubPage.Clip( i_rClipRect );
950 
951     // save gstate
952     o_rMtf.AddAction( new MetaPushAction( PUSH_ALL ) );
953 
954     // clip to page rect
955     o_rMtf.AddAction( new MetaClipRegionAction( Region( i_rClipRect ), sal_True ) );
956 
957     // append the subpage
958     io_rSubPage.WindStart();
959     io_rSubPage.Play( o_rMtf );
960 
961     // restore gstate
962     o_rMtf.AddAction( new MetaPopAction() );
963 
964     // draw a border
965     if( i_bDrawBorder )
966     {
967         // save gstate
968         o_rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_CLIPREGION | PUSH_MAPMODE ) );
969         o_rMtf.AddAction( new MetaMapModeAction( MapMode( MAP_100TH_MM ) ) );
970 
971         Rectangle aBorderRect( i_rClipRect );
972         o_rMtf.AddAction( new MetaLineColorAction( Color( COL_BLACK ), sal_True ) );
973         o_rMtf.AddAction( new MetaFillColorAction( Color( COL_TRANSPARENT ), sal_False ) );
974         o_rMtf.AddAction( new MetaRectAction( aBorderRect ) );
975 
976         // restore gstate
977         o_rMtf.AddAction( new MetaPopAction() );
978     }
979 }
980 
981 PrinterController::PageSize PrinterController::getFilteredPageFile( int i_nFilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
982 {
983     const MultiPageSetup& rMPS( mpImplData->maMultiPage );
984     int nSubPages = rMPS.nRows * rMPS.nColumns;
985     if( nSubPages < 1 )
986         nSubPages = 1;
987 
988     // reverse sheet order
989     if( mpImplData->mbReversePageOrder )
990     {
991         int nDocPages = getFilteredPageCount();
992         i_nFilteredPage = nDocPages - 1 - i_nFilteredPage;
993     }
994 
995     // there is no filtering to be done (and possibly the page size of the
996     // original page is to be set), when N-Up is "neutral" that is there is
997     // only one subpage and the margins are 0
998     if( nSubPages == 1 &&
999         rMPS.nLeftMargin == 0 && rMPS.nRightMargin == 0 &&
1000         rMPS.nTopMargin == 0 && rMPS.nBottomMargin == 0 )
1001     {
1002         PrinterController::PageSize aPageSize = getPageFile( i_nFilteredPage, o_rMtf, i_bMayUseCache );
1003         Size aPaperSize = mpImplData->getRealPaperSize( aPageSize.aSize, true );
1004         mpImplData->mpPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
1005         mpImplData->mpPrinter->SetPaperSizeUser( aPaperSize, ! mpImplData->isFixedPageSize() );
1006         if( aPaperSize != aPageSize.aSize )
1007         {
1008             // user overridden page size, center Metafile
1009             o_rMtf.WindStart();
1010             long nDX = (aPaperSize.Width() - aPageSize.aSize.Width()) / 2;
1011             long nDY = (aPaperSize.Height() - aPageSize.aSize.Height()) / 2;
1012             o_rMtf.Move( nDX, nDY, mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() );
1013             o_rMtf.WindStart();
1014             o_rMtf.SetPrefSize( aPaperSize );
1015             aPageSize.aSize = aPaperSize;
1016         }
1017         return aPageSize;
1018     }
1019 
1020     // set last page property really only on the very last page to be rendered
1021     // that is on the last subpage of a NUp run
1022     sal_Bool bIsLastPage = mpImplData->mbLastPage;
1023     mpImplData->mbLastPage = sal_False;
1024 
1025     Size aPaperSize( mpImplData->getRealPaperSize( mpImplData->maMultiPage.aPaperSize, false ) );
1026 
1027     // multi page area: page size minus margins + one time spacing right and down
1028     // the added spacing is so each subpage can be calculated including its spacing
1029     Size aMPArea( aPaperSize );
1030     aMPArea.Width()  -= rMPS.nLeftMargin + rMPS.nRightMargin;
1031     aMPArea.Width()  += rMPS.nHorizontalSpacing;
1032     aMPArea.Height() -= rMPS.nTopMargin + rMPS.nBottomMargin;
1033     aMPArea.Height() += rMPS.nVerticalSpacing;
1034 
1035     // determine offsets
1036     long nAdvX = aMPArea.Width() / rMPS.nColumns;
1037     long nAdvY = aMPArea.Height() / rMPS.nRows;
1038 
1039     // determine size of a "cell" subpage, leave a little space around pages
1040     Size aSubPageSize( nAdvX - rMPS.nHorizontalSpacing, nAdvY - rMPS.nVerticalSpacing );
1041 
1042     o_rMtf.Clear();
1043     o_rMtf.SetPrefSize( aPaperSize );
1044     o_rMtf.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
1045     o_rMtf.AddAction( new MetaMapModeAction( MapMode( MAP_100TH_MM ) ) );
1046 
1047     int nDocPages = getPageCountProtected();
1048     for( int nSubPage = 0; nSubPage < nSubPages; nSubPage++ )
1049     {
1050         // map current sub page to real page
1051         int nPage = (i_nFilteredPage * nSubPages + nSubPage) / rMPS.nRepeat;
1052         if( nSubPage == nSubPages-1 ||
1053             nPage == nDocPages-1 )
1054         {
1055             mpImplData->mbLastPage = bIsLastPage;
1056         }
1057         if( nPage >= 0 && nPage < nDocPages )
1058         {
1059             GDIMetaFile aPageFile;
1060             PrinterController::PageSize aPageSize = getPageFile( nPage, aPageFile, i_bMayUseCache );
1061             if( aPageSize.aSize.Width() && aPageSize.aSize.Height() )
1062             {
1063                 long nCellX = 0, nCellY = 0;
1064                 switch( rMPS.nOrder )
1065                 {
1066                 case PrinterController::LRTB:
1067                     nCellX = (nSubPage % rMPS.nColumns);
1068                     nCellY = (nSubPage / rMPS.nColumns);
1069                     break;
1070                 case PrinterController::TBLR:
1071                     nCellX = (nSubPage / rMPS.nRows);
1072                     nCellY = (nSubPage % rMPS.nRows);
1073                     break;
1074                 case PrinterController::RLTB:
1075                     nCellX = rMPS.nColumns - 1 - (nSubPage % rMPS.nColumns);
1076                     nCellY = (nSubPage / rMPS.nColumns);
1077                     break;
1078                 case PrinterController::TBRL:
1079                     nCellX = rMPS.nColumns - 1 - (nSubPage / rMPS.nRows);
1080                     nCellY = (nSubPage % rMPS.nRows);
1081                     break;
1082                 }
1083                 // scale the metafile down to a sub page size
1084                 double fScaleX = double(aSubPageSize.Width())/double(aPageSize.aSize.Width());
1085                 double fScaleY = double(aSubPageSize.Height())/double(aPageSize.aSize.Height());
1086                 double fScale  = std::min( fScaleX, fScaleY );
1087                 aPageFile.Scale( fScale, fScale );
1088                 aPageFile.WindStart();
1089 
1090                 // move the subpage so it is centered in its "cell"
1091                 long nOffX = (aSubPageSize.Width() - long(double(aPageSize.aSize.Width()) * fScale)) / 2;
1092                 long nOffY = (aSubPageSize.Height() - long(double(aPageSize.aSize.Height()) * fScale)) / 2;
1093                 long nX = rMPS.nLeftMargin + nOffX + nAdvX * nCellX;
1094                 long nY = rMPS.nTopMargin + nOffY + nAdvY * nCellY;
1095                 aPageFile.Move( nX, nY, mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() );
1096                 aPageFile.WindStart();
1097                 // calculate border rectangle
1098                 Rectangle aSubPageRect( Point( nX, nY ),
1099                                         Size( long(double(aPageSize.aSize.Width())*fScale),
1100                                               long(double(aPageSize.aSize.Height())*fScale) ) );
1101 
1102                 // append subpage to page
1103                 appendSubPage( o_rMtf, aSubPageRect, aPageFile, rMPS.bDrawBorder );
1104             }
1105         }
1106     }
1107     o_rMtf.WindStart();
1108 
1109     // subsequent getPageFile calls have changed the paper, reset it to current value
1110     mpImplData->mpPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
1111     mpImplData->mpPrinter->SetPaperSizeUser( aPaperSize, ! mpImplData->isFixedPageSize() );
1112 
1113     return PrinterController::PageSize( aPaperSize, true );
1114 }
1115 
1116 int PrinterController::getFilteredPageCount()
1117 {
1118     int nDiv = mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns;
1119     if( nDiv < 1 )
1120         nDiv = 1;
1121     return (getPageCountProtected() * mpImplData->maMultiPage.nRepeat + (nDiv-1)) / nDiv;
1122 }
1123 
1124 sal_uLong PrinterController::removeTransparencies( GDIMetaFile& i_rIn, GDIMetaFile& o_rOut )
1125 {
1126     sal_uLong nRestoreDrawMode = mpImplData->mpPrinter->GetDrawMode();
1127     sal_Int32 nMaxBmpDPIX = mpImplData->mpPrinter->ImplGetDPIX();
1128     sal_Int32 nMaxBmpDPIY = mpImplData->mpPrinter->ImplGetDPIY();
1129 
1130     const PrinterOptions&   rPrinterOptions = mpImplData->mpPrinter->GetPrinterOptions();
1131 
1132     static const sal_Int32 OPTIMAL_BMP_RESOLUTION = 300;
1133     static const sal_Int32 NORMAL_BMP_RESOLUTION  = 200;
1134 
1135 
1136     if( rPrinterOptions.IsReduceBitmaps() )
1137     {
1138         // calculate maximum resolution for bitmap graphics
1139         if( PRINTER_BITMAP_OPTIMAL == rPrinterOptions.GetReducedBitmapMode() )
1140         {
1141             nMaxBmpDPIX = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1142             nMaxBmpDPIY = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1143         }
1144         else if( PRINTER_BITMAP_NORMAL == rPrinterOptions.GetReducedBitmapMode() )
1145         {
1146             nMaxBmpDPIX = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1147             nMaxBmpDPIY = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1148         }
1149         else
1150         {
1151             nMaxBmpDPIX = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIX );
1152             nMaxBmpDPIY = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIY );
1153         }
1154     }
1155 
1156     // convert to greysacles
1157     if( rPrinterOptions.IsConvertToGreyscales() )
1158     {
1159         mpImplData->mpPrinter->SetDrawMode( mpImplData->mpPrinter->GetDrawMode() |
1160                                             ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
1161                                               DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
1162     }
1163 
1164     // disable transparency output
1165     if( rPrinterOptions.IsReduceTransparency() && ( PRINTER_TRANSPARENCY_NONE == rPrinterOptions.GetReducedTransparencyMode() ) )
1166     {
1167         mpImplData->mpPrinter->SetDrawMode( mpImplData->mpPrinter->GetDrawMode() | DRAWMODE_NOTRANSPARENCY );
1168     }
1169 
1170     Color aBg( COL_TRANSPARENT ); // default: let RemoveTransparenciesFromMetaFile do its own background logic
1171     if( mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns > 1 )
1172     {
1173         // in N-Up printing we have no "page" background operation
1174         // we also have no way to determine the paper color
1175         // so let's go for white, which will kill 99.9% of the real cases
1176         aBg = Color( COL_WHITE );
1177     }
1178     mpImplData->mpPrinter->RemoveTransparenciesFromMetaFile( i_rIn, o_rOut, nMaxBmpDPIX, nMaxBmpDPIY,
1179                                                              rPrinterOptions.IsReduceTransparency(),
1180                                                              rPrinterOptions.GetReducedTransparencyMode() == PRINTER_TRANSPARENCY_AUTO,
1181                                                              rPrinterOptions.IsReduceBitmaps() && rPrinterOptions.IsReducedBitmapIncludesTransparency(),
1182                                                              aBg
1183                                                              );
1184     return nRestoreDrawMode;
1185 }
1186 
1187 void PrinterController::printFilteredPage( int i_nPage )
1188 {
1189     if( mpImplData->meJobState != view::PrintableState_JOB_STARTED )
1190         return;
1191 
1192     GDIMetaFile aPageFile;
1193     PrinterController::PageSize aPageSize = getFilteredPageFile( i_nPage, aPageFile );
1194 
1195     if( mpImplData->mpProgress )
1196     {
1197         // do nothing if printing is canceled
1198         if( mpImplData->mpProgress->isCanceled() )
1199         {
1200             setJobState( view::PrintableState_JOB_ABORTED );
1201             return;
1202         }
1203     }
1204 
1205     // in N-Up printing set the correct page size
1206     mpImplData->mpPrinter->SetMapMode( MAP_100TH_MM );
1207 	// aPageSize was filtered through mpImplData->getRealPaperSize already by getFilteredPageFile()
1208     mpImplData->mpPrinter->SetPaperSizeUser( aPageSize.aSize, ! mpImplData->isFixedPageSize() );
1209     if( mpImplData->mnFixedPaperBin != -1 &&
1210         mpImplData->mpPrinter->GetPaperBin() != mpImplData->mnFixedPaperBin )
1211     {
1212         mpImplData->mpPrinter->SetPaperBin( mpImplData->mnFixedPaperBin );
1213     }
1214 
1215     // if full paper is meant to be used, move the output to accomodate for pageoffset
1216     if( aPageSize.bFullPaper )
1217     {
1218         Point aPageOffset( mpImplData->mpPrinter->GetPageOffset() );
1219         aPageFile.WindStart();
1220         aPageFile.Move( -aPageOffset.X(), -aPageOffset.Y(), mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() );
1221     }
1222 
1223     GDIMetaFile aCleanedFile;
1224     sal_uLong nRestoreDrawMode = removeTransparencies( aPageFile, aCleanedFile );
1225 
1226     mpImplData->mpPrinter->EnableOutput( sal_True );
1227 
1228     // actually print the page
1229     mpImplData->mpPrinter->ImplStartPage();
1230 
1231     mpImplData->mpPrinter->Push();
1232     aCleanedFile.WindStart();
1233     aCleanedFile.Play( mpImplData->mpPrinter.get() );
1234     mpImplData->mpPrinter->Pop();
1235 
1236     mpImplData->mpPrinter->ImplEndPage();
1237 
1238     mpImplData->mpPrinter->SetDrawMode( nRestoreDrawMode );
1239 }
1240 
1241 void PrinterController::jobStarted()
1242 {
1243 }
1244 
1245 void PrinterController::jobFinished( view::PrintableState )
1246 {
1247 }
1248 
1249 void PrinterController::abortJob()
1250 {
1251     setJobState( view::PrintableState_JOB_ABORTED );
1252     // applications (well, sw) depend on a page request with "IsLastPage" = true
1253     // to free resources, else they (well, sw) will crash eventually
1254     setLastPage( sal_True );
1255     delete mpImplData->mpProgress;
1256     mpImplData->mpProgress = NULL;
1257     GDIMetaFile aMtf;
1258     getPageFile( 0, aMtf, false );
1259 }
1260 
1261 void PrinterController::setLastPage( sal_Bool i_bLastPage )
1262 {
1263     mpImplData->mbLastPage = i_bLastPage;
1264 }
1265 
1266 void PrinterController::setReversePrint( sal_Bool i_bReverse )
1267 {
1268     mpImplData->mbReversePageOrder = i_bReverse;
1269 }
1270 
1271 bool PrinterController::getReversePrint() const
1272 {
1273     return mpImplData->mbReversePageOrder;
1274 }
1275 
1276 Sequence< PropertyValue > PrinterController::getJobProperties( const Sequence< PropertyValue >& i_rMergeList ) const
1277 {
1278     std::hash_set< rtl::OUString, rtl::OUStringHash > aMergeSet;
1279     size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3;
1280     for( int i = 0; i < i_rMergeList.getLength(); i++ )
1281         aMergeSet.insert( i_rMergeList[i].Name );
1282 
1283     Sequence< PropertyValue > aResult( nResultLen );
1284     for( int i = 0; i < i_rMergeList.getLength(); i++ )
1285         aResult[i] = i_rMergeList[i];
1286     int nCur = i_rMergeList.getLength();
1287     for( size_t i = 0; i < mpImplData->maUIProperties.size(); i++ )
1288     {
1289         if( aMergeSet.find( mpImplData->maUIProperties[i].Name ) == aMergeSet.end() )
1290             aResult[nCur++] = mpImplData->maUIProperties[i];
1291     }
1292     // append IsFirstPage
1293     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) ) ) == aMergeSet.end() )
1294     {
1295         PropertyValue aVal;
1296         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) );
1297         aVal.Value <<= mpImplData->mbFirstPage;
1298         aResult[nCur++] = aVal;
1299     }
1300     // append IsLastPage
1301     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) ) ) == aMergeSet.end() )
1302     {
1303         PropertyValue aVal;
1304         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) );
1305         aVal.Value <<= mpImplData->mbLastPage;
1306         aResult[nCur++] = aVal;
1307     }
1308     // append IsPrinter
1309     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) ) ) == aMergeSet.end() )
1310     {
1311         PropertyValue aVal;
1312         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) );
1313         aVal.Value <<= sal_True;
1314         aResult[nCur++] = aVal;
1315     }
1316     aResult.realloc( nCur );
1317     return aResult;
1318 }
1319 
1320 const Sequence< beans::PropertyValue >& PrinterController::getUIOptions() const
1321 {
1322     return mpImplData->maUIOptions;
1323 }
1324 
1325 beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty )
1326 {
1327     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1328         mpImplData->maPropertyToIndex.find( i_rProperty );
1329     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL;
1330 }
1331 
1332 const beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty ) const
1333 {
1334     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1335         mpImplData->maPropertyToIndex.find( i_rProperty );
1336     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL;
1337 }
1338 
1339 Sequence< beans::PropertyValue > PrinterController::getValues( const Sequence< rtl::OUString >& i_rNames ) const
1340 {
1341     Sequence< beans::PropertyValue > aRet( i_rNames.getLength() );
1342     sal_Int32 nFound = 0;
1343     for( sal_Int32 i = 0; i < i_rNames.getLength(); i++ )
1344     {
1345         const beans::PropertyValue* pVal = getValue( i_rNames[i] );
1346         if( pVal )
1347             aRet[ nFound++ ] = *pVal;
1348     }
1349     aRet.realloc( nFound );
1350     return aRet;
1351 }
1352 
1353 void PrinterController::setValue( const rtl::OUString& i_rName, const Any& i_rValue )
1354 {
1355     beans::PropertyValue aVal;
1356     aVal.Name = i_rName;
1357     aVal.Value = i_rValue;
1358 
1359     setValue( aVal );
1360 }
1361 
1362 void PrinterController::setValue( const beans::PropertyValue& i_rValue )
1363 {
1364     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1365         mpImplData->maPropertyToIndex.find( i_rValue.Name );
1366     if( it != mpImplData->maPropertyToIndex.end() )
1367         mpImplData->maUIProperties[ it->second ] = i_rValue;
1368     else
1369     {
1370         // insert correct index into property map
1371         mpImplData->maPropertyToIndex[ i_rValue.Name ] = mpImplData->maUIProperties.size();
1372         mpImplData->maUIProperties.push_back( i_rValue );
1373         mpImplData->maUIPropertyEnabled.push_back( true );
1374     }
1375 }
1376 
1377 void PrinterController::setUIOptions( const Sequence< beans::PropertyValue >& i_rOptions )
1378 {
1379     DBG_ASSERT( mpImplData->maUIOptions.getLength() == 0, "setUIOptions called twice !" );
1380 
1381     mpImplData->maUIOptions = i_rOptions;
1382 
1383     for( int i = 0; i < i_rOptions.getLength(); i++ )
1384     {
1385         Sequence< beans::PropertyValue > aOptProp;
1386         i_rOptions[i].Value >>= aOptProp;
1387         bool bIsEnabled = true;
1388         bool bHaveProperty = false;
1389         rtl::OUString aPropName;
1390         vcl::ImplPrinterControllerData::ControlDependency aDep;
1391         Sequence< sal_Bool > aChoicesDisabled;
1392         for( int n = 0; n < aOptProp.getLength(); n++ )
1393         {
1394             const beans::PropertyValue& rEntry( aOptProp[ n ] );
1395             if( rEntry.Name.equalsAscii( "Property" ) )
1396             {
1397                 PropertyValue aVal;
1398                 rEntry.Value >>= aVal;
1399                 DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name )
1400                             == mpImplData->maPropertyToIndex.end(), "duplicate property entry" );
1401                 setValue( aVal );
1402                 aPropName = aVal.Name;
1403                 bHaveProperty = true;
1404             }
1405             else if( rEntry.Name.equalsAscii( "Enabled" ) )
1406             {
1407                 sal_Bool bValue = sal_True;
1408                 rEntry.Value >>= bValue;
1409                 bIsEnabled = bValue;
1410             }
1411             else if( rEntry.Name.equalsAscii( "DependsOnName" ) )
1412             {
1413                 rEntry.Value >>= aDep.maDependsOnName;
1414             }
1415             else if( rEntry.Name.equalsAscii( "DependsOnEntry" ) )
1416             {
1417                 rEntry.Value >>= aDep.mnDependsOnEntry;
1418             }
1419             else if( rEntry.Name.equalsAscii( "ChoicesDisabled" ) )
1420             {
1421                 rEntry.Value >>= aChoicesDisabled;
1422             }
1423         }
1424         if( bHaveProperty )
1425         {
1426             vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it =
1427                 mpImplData->maPropertyToIndex.find( aPropName );
1428             // sanity check
1429             if( it != mpImplData->maPropertyToIndex.end() )
1430             {
1431                 mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled;
1432             }
1433             if( aDep.maDependsOnName.getLength() > 0 )
1434                 mpImplData->maControlDependencies[ aPropName ] = aDep;
1435             if( aChoicesDisabled.getLength() > 0 )
1436                 mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled;
1437         }
1438     }
1439 }
1440 
1441 void PrinterController::enableUIOption( const rtl::OUString& i_rProperty, bool i_bEnable )
1442 {
1443     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1444         mpImplData->maPropertyToIndex.find( i_rProperty );
1445     if( it != mpImplData->maPropertyToIndex.end() )
1446     {
1447         // call handler only for actual changes
1448         if( ( mpImplData->maUIPropertyEnabled[ it->second ] && ! i_bEnable ) ||
1449             ( ! mpImplData->maUIPropertyEnabled[ it->second ] && i_bEnable ) )
1450         {
1451             mpImplData->maUIPropertyEnabled[ it->second ] = i_bEnable;
1452             rtl::OUString aPropName( i_rProperty );
1453             mpImplData->maOptionChangeHdl.Call( &aPropName );
1454         }
1455     }
1456 }
1457 
1458 bool PrinterController::isUIOptionEnabled( const rtl::OUString& i_rProperty ) const
1459 {
1460     bool bEnabled = false;
1461     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator prop_it =
1462         mpImplData->maPropertyToIndex.find( i_rProperty );
1463     if( prop_it != mpImplData->maPropertyToIndex.end() )
1464     {
1465         bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second];
1466 
1467         if( bEnabled )
1468         {
1469             // check control dependencies
1470             vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1471                 mpImplData->maControlDependencies.find( i_rProperty );
1472             if( it != mpImplData->maControlDependencies.end() )
1473             {
1474                 // check if the dependency is enabled
1475                 // if the dependency is disabled, we are too
1476                 bEnabled = isUIOptionEnabled( it->second.maDependsOnName );
1477 
1478                 if( bEnabled )
1479                 {
1480                     // does the dependency have the correct value ?
1481                     const com::sun::star::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName );
1482                     OSL_ENSURE( pVal, "unknown property in dependency" );
1483                     if( pVal )
1484                     {
1485                         sal_Int32 nDepVal = 0;
1486                         sal_Bool bDepVal = sal_False;
1487                         if( pVal->Value >>= nDepVal )
1488                         {
1489                             bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1);
1490                         }
1491                         else if( pVal->Value >>= bDepVal )
1492                         {
1493                             // could be a dependency on a checked boolean
1494                             // in this case the dependency is on a non zero for checked value
1495                             bEnabled = (   bDepVal && it->second.mnDependsOnEntry != 0) ||
1496                                        ( ! bDepVal && it->second.mnDependsOnEntry == 0);
1497                         }
1498                         else
1499                         {
1500                             // if the type does not match something is awry
1501                             OSL_ENSURE( 0, "strange type in control dependency" );
1502                             bEnabled = false;
1503                         }
1504                     }
1505                 }
1506             }
1507         }
1508     }
1509     return bEnabled;
1510 }
1511 
1512 bool PrinterController::isUIChoiceEnabled( const rtl::OUString& i_rProperty, sal_Int32 i_nValue ) const
1513 {
1514     bool bEnabled = true;
1515     ImplPrinterControllerData::ChoiceDisableMap::const_iterator it =
1516         mpImplData->maChoiceDisableMap.find( i_rProperty );
1517     if(it != mpImplData->maChoiceDisableMap.end() )
1518     {
1519         const Sequence< sal_Bool >& rDisabled( it->second );
1520         if( i_nValue >= 0 && i_nValue < rDisabled.getLength() )
1521             bEnabled = ! rDisabled[i_nValue];
1522     }
1523     return bEnabled;
1524 }
1525 
1526 rtl::OUString PrinterController::getDependency( const rtl::OUString& i_rProperty ) const
1527 {
1528     rtl::OUString aDependency;
1529 
1530     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1531         mpImplData->maControlDependencies.find( i_rProperty );
1532     if( it != mpImplData->maControlDependencies.end() )
1533         aDependency = it->second.maDependsOnName;
1534 
1535     return aDependency;
1536 }
1537 
1538 rtl::OUString PrinterController::makeEnabled( const rtl::OUString& i_rProperty )
1539 {
1540     rtl::OUString aDependency;
1541 
1542     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1543         mpImplData->maControlDependencies.find( i_rProperty );
1544     if( it != mpImplData->maControlDependencies.end() )
1545     {
1546         if( isUIOptionEnabled( it->second.maDependsOnName ) )
1547         {
1548            aDependency = it->second.maDependsOnName;
1549            const com::sun::star::beans::PropertyValue* pVal = getValue( aDependency );
1550            OSL_ENSURE( pVal, "unknown property in dependency" );
1551            if( pVal )
1552            {
1553                sal_Int32 nDepVal = 0;
1554                sal_Bool bDepVal = sal_False;
1555                if( pVal->Value >>= nDepVal )
1556                {
1557                    if( it->second.mnDependsOnEntry != -1 )
1558                    {
1559                        setValue( aDependency, makeAny( sal_Int32( it->second.mnDependsOnEntry ) ) );
1560                    }
1561                }
1562                else if( pVal->Value >>= bDepVal )
1563                {
1564                    setValue( aDependency, makeAny( sal_Bool( it->second.mnDependsOnEntry != 0 ) ) );
1565                }
1566                else
1567                {
1568                    // if the type does not match something is awry
1569                    OSL_ENSURE( 0, "strange type in control dependency" );
1570                }
1571            }
1572         }
1573     }
1574 
1575     return aDependency;
1576 }
1577 
1578 void PrinterController::setOptionChangeHdl( const Link& i_rHdl )
1579 {
1580     mpImplData->maOptionChangeHdl = i_rHdl;
1581 }
1582 
1583 void PrinterController::createProgressDialog()
1584 {
1585     if( ! mpImplData->mpProgress )
1586     {
1587         sal_Bool bShow = sal_True;
1588         beans::PropertyValue* pMonitor = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MonitorVisible" ) ) );
1589         if( pMonitor )
1590             pMonitor->Value >>= bShow;
1591         else
1592         {
1593             const com::sun::star::beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ) );
1594             if( pVal )
1595             {
1596                 sal_Bool bApi = sal_False;
1597                 pVal->Value >>= bApi;
1598                 bShow = ! bApi;
1599             }
1600         }
1601 
1602         if( bShow && ! Application::IsHeadlessModeEnabled() )
1603         {
1604             mpImplData->mpProgress = new PrintProgressDialog( NULL, getPageCountProtected() );
1605             mpImplData->mpProgress->Show();
1606         }
1607     }
1608     else
1609         mpImplData->mpProgress->reset();
1610 }
1611 
1612 bool PrinterController::isProgressCanceled() const
1613 {
1614     return mpImplData->mpProgress && mpImplData->mpProgress->isCanceled();
1615 }
1616 
1617 void PrinterController::setMultipage( const MultiPageSetup& i_rMPS )
1618 {
1619     mpImplData->maMultiPage = i_rMPS;
1620 }
1621 
1622 const PrinterController::MultiPageSetup& PrinterController::getMultipage() const
1623 {
1624     return mpImplData->maMultiPage;
1625 }
1626 
1627 void PrinterController::pushPropertiesToPrinter()
1628 {
1629     sal_Int32 nCopyCount = 1;
1630     // set copycount and collate
1631     const beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CopyCount" ) ) );
1632     if( pVal )
1633         pVal->Value >>= nCopyCount;
1634     sal_Bool bCollate = sal_False;
1635     pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) );
1636     if( pVal )
1637         pVal->Value >>= bCollate;
1638     mpImplData->mpPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate );
1639 
1640     // duplex mode
1641     pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DuplexMode" ) ) );
1642     if( pVal )
1643     {
1644         sal_Int16 nDuplex = view::DuplexMode::UNKNOWN;
1645         pVal->Value >>= nDuplex;
1646         switch( nDuplex )
1647         {
1648         case view::DuplexMode::OFF: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_OFF ); break;
1649         case view::DuplexMode::LONGEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_LONGEDGE ); break;
1650         case view::DuplexMode::SHORTEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_SHORTEDGE ); break;
1651         }
1652     }
1653 }
1654 
1655 bool PrinterController::isShowDialogs() const
1656 {
1657     sal_Bool bApi = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ), sal_False );
1658     return ! bApi && ! Application::IsHeadlessModeEnabled();
1659 }
1660 
1661 bool PrinterController::isDirectPrint() const
1662 {
1663     sal_Bool bDirect = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDirect" ) ), sal_False );
1664     return bDirect == sal_True;
1665 }
1666 
1667 sal_Bool PrinterController::getBoolProperty( const rtl::OUString& i_rProperty, sal_Bool i_bFallback ) const
1668 {
1669     sal_Bool bRet = i_bFallback;
1670     const com::sun::star::beans::PropertyValue* pVal = getValue( i_rProperty );
1671     if( pVal )
1672         pVal->Value >>= bRet;
1673     return bRet;
1674 }
1675 
1676 sal_Int32 PrinterController::getIntProperty( const rtl::OUString& i_rProperty, sal_Int32 i_nFallback ) const
1677 {
1678     sal_Int32 nRet = i_nFallback;
1679     const com::sun::star::beans::PropertyValue* pVal = getValue( i_rProperty );
1680     if( pVal )
1681         pVal->Value >>= nRet;
1682     return nRet;
1683 }
1684 
1685 /*
1686  * PrinterOptionsHelper
1687 **/
1688 Any PrinterOptionsHelper::getValue( const rtl::OUString& i_rPropertyName ) const
1689 {
1690     Any aRet;
1691     std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it =
1692         m_aPropertyMap.find( i_rPropertyName );
1693     if( it != m_aPropertyMap.end() )
1694         aRet = it->second;
1695     return aRet;
1696 }
1697 
1698 void PrinterOptionsHelper::setValue( const rtl::OUString& i_rPropertyName, const Any& i_rValue )
1699 {
1700     m_aPropertyMap[ i_rPropertyName ] = i_rValue;
1701 }
1702 
1703 bool PrinterOptionsHelper::hasProperty( const rtl::OUString& i_rPropertyName ) const
1704 {
1705     Any aRet;
1706     std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it =
1707         m_aPropertyMap.find( i_rPropertyName );
1708     return it != m_aPropertyMap.end();
1709 }
1710 
1711 sal_Bool PrinterOptionsHelper::getBoolValue( const rtl::OUString& i_rPropertyName, sal_Bool i_bDefault ) const
1712 {
1713     sal_Bool bRet = sal_False;
1714     Any aVal( getValue( i_rPropertyName ) );
1715     return (aVal >>= bRet) ? bRet : i_bDefault;
1716 }
1717 
1718 sal_Int64 PrinterOptionsHelper::getIntValue( const rtl::OUString& i_rPropertyName, sal_Int64 i_nDefault ) const
1719 {
1720     sal_Int64 nRet = 0;
1721     Any aVal( getValue( i_rPropertyName ) );
1722     return (aVal >>= nRet) ? nRet : i_nDefault;
1723 }
1724 
1725 rtl::OUString PrinterOptionsHelper::getStringValue( const rtl::OUString& i_rPropertyName, const rtl::OUString& i_rDefault ) const
1726 {
1727     rtl::OUString aRet;
1728     Any aVal( getValue( i_rPropertyName ) );
1729     return (aVal >>= aRet) ? aRet : i_rDefault;
1730 }
1731 
1732 bool PrinterOptionsHelper::processProperties( const Sequence< PropertyValue >& i_rNewProp,
1733                                               std::set< rtl::OUString >* o_pChangeProp )
1734 {
1735     bool bChanged = false;
1736 
1737     // clear the changed set
1738     if( o_pChangeProp )
1739         o_pChangeProp->clear();
1740 
1741     sal_Int32 nElements = i_rNewProp.getLength();
1742     const PropertyValue* pVals = i_rNewProp.getConstArray();
1743     for( sal_Int32 i = 0; i < nElements; i++ )
1744     {
1745         bool bElementChanged = false;
1746         std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::iterator it =
1747             m_aPropertyMap.find( pVals[ i ].Name );
1748         if( it != m_aPropertyMap.end() )
1749         {
1750             if( it->second != pVals[ i ].Value )
1751                 bElementChanged = true;
1752         }
1753         else
1754             bElementChanged = true;
1755 
1756         if( bElementChanged )
1757         {
1758             if( o_pChangeProp )
1759                 o_pChangeProp->insert( pVals[ i ].Name );
1760             m_aPropertyMap[ pVals[i].Name ] = pVals[i].Value;
1761             bChanged = true;
1762         }
1763     }
1764     return bChanged;
1765 }
1766 
1767 void PrinterOptionsHelper::appendPrintUIOptions( uno::Sequence< beans::PropertyValue >& io_rProps ) const
1768 {
1769     if( m_aUIProperties.getLength() > 0 )
1770     {
1771         sal_Int32 nIndex = io_rProps.getLength();
1772         io_rProps.realloc( nIndex+1 );
1773         PropertyValue aVal;
1774         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExtraPrintUIOptions" ) );
1775         aVal.Value = makeAny( m_aUIProperties );
1776         io_rProps[ nIndex ] = aVal;
1777     }
1778 }
1779 
1780 Any PrinterOptionsHelper::getUIControlOpt( const rtl::OUString& i_rTitle,
1781                                            const Sequence< rtl::OUString >& i_rHelpIds,
1782                                            const rtl::OUString& i_rType,
1783                                            const PropertyValue* i_pVal,
1784                                            const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1785                                            )
1786 {
1787     sal_Int32 nElements =
1788         1                                                               // ControlType
1789         + (i_rTitle.getLength() ? 1 : 0)                                // Text
1790         + (i_rHelpIds.getLength() ? 1 : 0)                              // HelpId
1791         + (i_pVal ? 1 : 0)                                              // Property
1792         + i_rControlOptions.maAddProps.getLength()                      // additional props
1793         + (i_rControlOptions.maGroupHint.getLength() ? 1 : 0)           // grouping
1794         + (i_rControlOptions.mbInternalOnly ? 1 : 0)                    // internal hint
1795         + (i_rControlOptions.mbEnabled ? 0 : 1)                         // enabled
1796         ;
1797     if( i_rControlOptions.maDependsOnName.getLength() )
1798     {
1799         nElements += 1;
1800         if( i_rControlOptions.mnDependsOnEntry != -1 )
1801             nElements += 1;
1802         if( i_rControlOptions.mbAttachToDependency )
1803             nElements += 1;
1804     }
1805 
1806     Sequence< PropertyValue > aCtrl( nElements );
1807     sal_Int32 nUsed = 0;
1808     if( i_rTitle.getLength() )
1809     {
1810         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" ) );
1811         aCtrl[nUsed++].Value = makeAny( i_rTitle );
1812     }
1813     if( i_rHelpIds.getLength() )
1814     {
1815         aCtrl[nUsed  ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpId" ) );
1816         aCtrl[nUsed++].Value = makeAny( i_rHelpIds );
1817     }
1818     aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ControlType" ) );
1819     aCtrl[nUsed++].Value = makeAny( i_rType );
1820     if( i_pVal )
1821     {
1822         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Property" ) );
1823         aCtrl[nUsed++].Value = makeAny( *i_pVal );
1824     }
1825     if( i_rControlOptions.maDependsOnName.getLength() )
1826     {
1827         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnName" ) );
1828         aCtrl[nUsed++].Value = makeAny( i_rControlOptions.maDependsOnName );
1829         if( i_rControlOptions.mnDependsOnEntry != -1 )
1830         {
1831             aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnEntry" ) );
1832             aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mnDependsOnEntry );
1833         }
1834         if( i_rControlOptions.mbAttachToDependency )
1835         {
1836             aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AttachToDependency" ) );
1837             aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mbAttachToDependency );
1838         }
1839     }
1840     if( i_rControlOptions.maGroupHint.getLength() )
1841     {
1842         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GroupingHint" ) );
1843         aCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint;
1844     }
1845     if( i_rControlOptions.mbInternalOnly )
1846     {
1847         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "InternalUIOnly" ) );
1848         aCtrl[nUsed++].Value <<= sal_True;
1849     }
1850     if( ! i_rControlOptions.mbEnabled )
1851     {
1852         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Enabled" ) );
1853         aCtrl[nUsed++].Value <<= sal_False;
1854     }
1855 
1856     sal_Int32 nAddProps = i_rControlOptions.maAddProps.getLength();
1857     for( sal_Int32 i = 0; i < nAddProps; i++ )
1858         aCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i];
1859 
1860     DBG_ASSERT( nUsed == nElements, "nUsed != nElements, probable heap corruption" );
1861 
1862     return makeAny( aCtrl );
1863 }
1864 
1865 Any PrinterOptionsHelper::getGroupControlOpt( const rtl::OUString& i_rTitle, const rtl::OUString& i_rHelpId )
1866 {
1867     Sequence< rtl::OUString > aHelpId;
1868     if( i_rHelpId.getLength() > 0 )
1869     {
1870         aHelpId.realloc( 1 );
1871         *aHelpId.getArray() = i_rHelpId;
1872     }
1873     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Group" ) ) );
1874 }
1875 
1876 Any PrinterOptionsHelper::getSubgroupControlOpt( const rtl::OUString& i_rTitle,
1877                                                  const rtl::OUString& i_rHelpId,
1878                                                  const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1879                                                  )
1880 {
1881     Sequence< rtl::OUString > aHelpId;
1882     if( i_rHelpId.getLength() > 0 )
1883     {
1884         aHelpId.realloc( 1 );
1885         *aHelpId.getArray() = i_rHelpId;
1886     }
1887     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Subgroup" ) ),
1888                             NULL, i_rControlOptions );
1889 }
1890 
1891 Any PrinterOptionsHelper::getBoolControlOpt( const rtl::OUString& i_rTitle,
1892                                              const rtl::OUString& i_rHelpId,
1893                                              const rtl::OUString& i_rProperty,
1894                                              sal_Bool i_bValue,
1895                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1896                                              )
1897 {
1898     Sequence< rtl::OUString > aHelpId;
1899     if( i_rHelpId.getLength() > 0 )
1900     {
1901         aHelpId.realloc( 1 );
1902         *aHelpId.getArray() = i_rHelpId;
1903     }
1904     PropertyValue aVal;
1905     aVal.Name = i_rProperty;
1906     aVal.Value = makeAny( i_bValue );
1907     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Bool" ) ), &aVal, i_rControlOptions );
1908 }
1909 
1910 Any PrinterOptionsHelper::getChoiceControlOpt( const rtl::OUString& i_rTitle,
1911                                                const Sequence< rtl::OUString >& i_rHelpId,
1912                                                const rtl::OUString& i_rProperty,
1913                                                const Sequence< rtl::OUString >& i_rChoices,
1914                                                sal_Int32 i_nValue,
1915                                                const rtl::OUString& i_rType,
1916                                                const Sequence< sal_Bool >& i_rDisabledChoices,
1917                                                const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1918                                                )
1919 {
1920     UIControlOptions aOpt( i_rControlOptions );
1921     sal_Int32 nUsed = aOpt.maAddProps.getLength();
1922     aOpt.maAddProps.realloc( nUsed + 1 + (i_rDisabledChoices.getLength() ? 1 : 0) );
1923     aOpt.maAddProps[nUsed].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Choices" ) );
1924     aOpt.maAddProps[nUsed].Value = makeAny( i_rChoices );
1925     if( i_rDisabledChoices.getLength() )
1926     {
1927         aOpt.maAddProps[nUsed+1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ChoicesDisabled" ) );
1928         aOpt.maAddProps[nUsed+1].Value = makeAny( i_rDisabledChoices );
1929     }
1930 
1931     PropertyValue aVal;
1932     aVal.Name = i_rProperty;
1933     aVal.Value = makeAny( i_nValue );
1934     return getUIControlOpt( i_rTitle, i_rHelpId, i_rType, &aVal, aOpt );
1935 }
1936 
1937 Any PrinterOptionsHelper::getRangeControlOpt( const rtl::OUString& i_rTitle,
1938                                               const rtl::OUString& i_rHelpId,
1939                                               const rtl::OUString& i_rProperty,
1940                                               sal_Int32 i_nValue,
1941                                               sal_Int32 i_nMinValue,
1942                                               sal_Int32 i_nMaxValue,
1943                                               const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1944                                             )
1945 {
1946     UIControlOptions aOpt( i_rControlOptions );
1947     if( i_nMaxValue >= i_nMinValue )
1948     {
1949         sal_Int32 nUsed = aOpt.maAddProps.getLength();
1950         aOpt.maAddProps.realloc( nUsed + 2 );
1951         aOpt.maAddProps[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MinValue" ) );
1952         aOpt.maAddProps[nUsed++].Value = makeAny( i_nMinValue );
1953         aOpt.maAddProps[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MaxValue" ) );
1954         aOpt.maAddProps[nUsed++].Value = makeAny( i_nMaxValue );
1955     }
1956 
1957     Sequence< rtl::OUString > aHelpId;
1958     if( i_rHelpId.getLength() > 0 )
1959     {
1960         aHelpId.realloc( 1 );
1961         *aHelpId.getArray() = i_rHelpId;
1962     }
1963     PropertyValue aVal;
1964     aVal.Name = i_rProperty;
1965     aVal.Value = makeAny( i_nValue );
1966     return getUIControlOpt( i_rTitle,
1967                             aHelpId,
1968                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Range" ) ),
1969                             &aVal,
1970                             aOpt
1971                             );
1972 }
1973 
1974 Any PrinterOptionsHelper::getEditControlOpt( const rtl::OUString& i_rTitle,
1975                                              const rtl::OUString& i_rHelpId,
1976                                              const rtl::OUString& i_rProperty,
1977                                              const rtl::OUString& i_rValue,
1978                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1979                                            )
1980 {
1981     Sequence< rtl::OUString > aHelpId;
1982     if( i_rHelpId.getLength() > 0 )
1983     {
1984         aHelpId.realloc( 1 );
1985         *aHelpId.getArray() = i_rHelpId;
1986     }
1987     PropertyValue aVal;
1988     aVal.Name = i_rProperty;
1989     aVal.Value = makeAny( i_rValue );
1990     return getUIControlOpt( i_rTitle,
1991                             aHelpId,
1992                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Edit" ) ),
1993                             &aVal,
1994                             i_rControlOptions
1995                             );
1996 }
1997