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