xref: /aoo41x/main/vcl/source/gdi/print3.cxx (revision cdf0e10c)
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     // in direct print case check whether there is anything to print.
423     // if not, show an errorbox (if appropriate)
424     if( pController->isShowDialogs() && pController->isDirectPrint() )
425     {
426         if( pController->getFilteredPageCount() == 0 )
427         {
428             ErrorBox aBox( NULL, VclResId( SV_PRINT_NOCONTENT ) );
429             aBox.Execute();
430             return;
431         }
432     }
433 
434     // check if the printer brings up its own dialog
435     // in that case leave the work to that dialog
436     if( ! pController->getPrinter()->GetCapabilities( PRINTER_CAPABILITIES_EXTERNALDIALOG ) &&
437         ! pController->isDirectPrint() &&
438         pController->isShowDialogs()
439         )
440     {
441         try
442         {
443             PrintDialog aDlg( NULL, i_pController );
444             if( ! aDlg.Execute() )
445             {
446                 GDIMetaFile aPageFile;
447                 i_pController->abortJob();
448                 return;
449             }
450             if( aDlg.isPrintToFile() )
451             {
452                 rtl::OUString aFile = queryFile( pController->getPrinter().get() );
453                 if( ! aFile.getLength() )
454                 {
455                     i_pController->abortJob();
456                     return;
457                 }
458                 pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LocalFileName" ) ),
459                                        makeAny( aFile ) );
460             }
461             else if( aDlg.isSingleJobs() )
462             {
463                 pController->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ),
464                                        makeAny( sal_True ) );
465             }
466         }
467         catch( std::bad_alloc& )
468         {
469         }
470     }
471 
472     pController->pushPropertiesToPrinter();
473 
474     rtl::OUString aJobName;
475     beans::PropertyValue* pJobNameVal = pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "JobName" ) ) );
476     if( pJobNameVal )
477         pJobNameVal->Value >>= aJobName;
478 
479     pController->getPrinter()->StartJob( String( aJobName ), pController );
480 
481     pController->jobFinished( pController->getJobState() );
482 }
483 
484 bool Printer::StartJob( const rtl::OUString& i_rJobName, boost::shared_ptr<vcl::PrinterController>& i_pController )
485 {
486 	mnError = PRINTER_OK;
487 
488 	if ( IsDisplayPrinter() )
489 		return sal_False;
490 
491 	if ( IsJobActive() || IsPrinting() )
492 		return sal_False;
493 
494 	sal_uLong   nCopies = mnCopyCount;
495 	bool    bCollateCopy = mbCollateCopy;
496 	bool    bUserCopy = sal_False;
497 
498     if ( nCopies > 1 )
499     {
500         sal_uLong nDevCopy;
501 
502         if ( bCollateCopy )
503             nDevCopy = GetCapabilities( PRINTER_CAPABILITIES_COLLATECOPIES );
504         else
505             nDevCopy = GetCapabilities( PRINTER_CAPABILITIES_COPIES );
506 
507         // need to do copies by hand ?
508         if ( nCopies > nDevCopy )
509         {
510             bUserCopy = sal_True;
511             nCopies = 1;
512             bCollateCopy = sal_False;
513         }
514     }
515     else
516         bCollateCopy = sal_False;
517 
518 
519     ImplSVData* pSVData = ImplGetSVData();
520     mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
521 
522     if ( !mpPrinter )
523         return sal_False;
524 
525     sal_Bool bSinglePrintJobs = sal_False;
526     beans::PropertyValue* pSingleValue = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) );
527     if( pSingleValue )
528     {
529         pSingleValue->Value >>= bSinglePrintJobs;
530     }
531 
532     beans::PropertyValue* pFileValue = i_pController->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LocalFileName" ) ) );
533     if( pFileValue )
534     {
535         rtl::OUString aFile;
536         pFileValue->Value >>= aFile;
537         if( aFile.getLength() )
538         {
539             mbPrintFile = sal_True;
540             maPrintFile = aFile;
541             bSinglePrintJobs = sal_False;
542         }
543     }
544 
545     XubString* pPrintFile = NULL;
546     if ( mbPrintFile )
547         pPrintFile = &maPrintFile;
548     mpPrinterOptions->ReadFromConfig( mbPrintFile );
549 
550     maJobName		        = i_rJobName;
551     mnCurPage		        = 1;
552     mnCurPrintPage	        = 1;
553     mbPrinting		        = sal_True;
554     if( GetCapabilities( PRINTER_CAPABILITIES_USEPULLMODEL ) )
555     {
556         mbJobActive             = sal_True;
557         // sallayer does all necessary page printing
558         // and also handles showing a dialog
559         // that also means it must call jobStarted when the dialog is finished
560         // it also must set the JobState of the Controller
561         if( mpPrinter->StartJob( pPrintFile,
562                                  i_rJobName,
563                                  Application::GetDisplayName(),
564                                  maJobSetup.ImplGetConstData(),
565                                  *i_pController ) )
566         {
567             EndJob();
568         }
569         else
570         {
571             mnError = ImplSalPrinterErrorCodeToVCL( mpPrinter->GetErrorCode() );
572             if ( !mnError )
573                 mnError = PRINTER_GENERALERROR;
574             pSVData->mpDefInst->DestroyPrinter( mpPrinter );
575             mnCurPage		    = 0;
576             mnCurPrintPage	    = 0;
577             mbPrinting		    = sal_False;
578             mpPrinter = NULL;
579 
580             return false;
581         }
582     }
583     else
584     {
585         // possibly a dialog has been shown
586         // now the real job starts
587         i_pController->setJobState( view::PrintableState_JOB_STARTED );
588         i_pController->jobStarted();
589 
590         int nJobs = 1;
591         int nOuterRepeatCount = 1;
592         int nInnerRepeatCount = 1;
593         if( bUserCopy )
594         {
595             if( mbCollateCopy )
596                 nOuterRepeatCount = mnCopyCount;
597             else
598                 nInnerRepeatCount = mnCopyCount;
599         }
600         if( bSinglePrintJobs )
601         {
602             nJobs = mnCopyCount;
603             nCopies = 1;
604             nOuterRepeatCount = nInnerRepeatCount = 1;
605         }
606 
607         for( int nJobIteration = 0; nJobIteration < nJobs; nJobIteration++ )
608         {
609             bool bError = false, bAborted = false;
610             if( mpPrinter->StartJob( pPrintFile,
611                                      i_rJobName,
612                                      Application::GetDisplayName(),
613                                      nCopies,
614                                      bCollateCopy,
615                                      i_pController->isDirectPrint(),
616                                      maJobSetup.ImplGetConstData() ) )
617             {
618                 mbJobActive             = sal_True;
619                 i_pController->createProgressDialog();
620                 int nPages = i_pController->getFilteredPageCount();
621                 for( int nOuterIteration = 0; nOuterIteration < nOuterRepeatCount && ! bAborted; nOuterIteration++ )
622                 {
623                     for( int nPage = 0; nPage < nPages && ! bAborted; nPage++ )
624                     {
625                         for( int nInnerIteration = 0; nInnerIteration < nInnerRepeatCount && ! bAborted; nInnerIteration++ )
626                         {
627                             if( nPage == nPages-1 &&
628                                 nOuterIteration == nOuterRepeatCount-1 &&
629                                 nInnerIteration == nInnerRepeatCount-1 &&
630                                 nJobIteration == nJobs-1 )
631                             {
632                                 i_pController->setLastPage( sal_True );
633                             }
634                             i_pController->printFilteredPage( nPage );
635                             if( i_pController->isProgressCanceled() )
636                             {
637                                 i_pController->abortJob();
638                                 bAborted = true;
639                             }
640                         }
641                     }
642                     // FIXME: duplex ?
643                 }
644                 EndJob();
645 
646                 if( nJobIteration < nJobs-1 )
647                 {
648                     mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
649 
650                     if ( mpPrinter )
651                     {
652                         maJobName		        = i_rJobName;
653                         mnCurPage		        = 1;
654                         mnCurPrintPage	        = 1;
655                         mbPrinting		        = sal_True;
656                     }
657                     else
658                         bError = true;
659                 }
660             }
661             else
662                 bError = true;
663 
664             if( bError )
665             {
666                 mnError = ImplSalPrinterErrorCodeToVCL( mpPrinter->GetErrorCode() );
667                 if ( !mnError )
668                     mnError = PRINTER_GENERALERROR;
669                 i_pController->setJobState( mnError == PRINTER_ABORT
670                                             ? view::PrintableState_JOB_ABORTED
671                                             : view::PrintableState_JOB_FAILED );
672                 if( mpPrinter )
673                     pSVData->mpDefInst->DestroyPrinter( mpPrinter );
674                 mnCurPage		    = 0;
675                 mnCurPrintPage	    = 0;
676                 mbPrinting		    = sal_False;
677                 mpPrinter = NULL;
678 
679                 return false;
680             }
681         }
682 
683         if( i_pController->getJobState() == view::PrintableState_JOB_STARTED )
684             i_pController->setJobState( view::PrintableState_JOB_SPOOLED );
685     }
686 
687     // make last used printer persistent for UI jobs
688     if( i_pController->isShowDialogs() && ! i_pController->isDirectPrint() )
689     {
690         SettingsConfigItem* pItem = SettingsConfigItem::get();
691         pItem->setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintDialog" ) ),
692                          rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "LastPrinterUsed" ) ),
693                          GetName()
694                          );
695     }
696 
697 	return true;
698 }
699 
700 PrinterController::~PrinterController()
701 {
702     delete mpImplData;
703 }
704 
705 view::PrintableState PrinterController::getJobState() const
706 {
707     return mpImplData->meJobState;
708 }
709 
710 void PrinterController::setJobState( view::PrintableState i_eState )
711 {
712     mpImplData->meJobState = i_eState;
713 }
714 
715 const boost::shared_ptr<Printer>& PrinterController::getPrinter() const
716 {
717     return mpImplData->mpPrinter;
718 }
719 
720 void PrinterController::setPrinter( const boost::shared_ptr<Printer>& i_rPrinter )
721 {
722     mpImplData->mpPrinter = i_rPrinter;
723     setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Name" ) ),
724               makeAny( rtl::OUString( i_rPrinter->GetName() ) ) );
725     mpImplData->mnDefaultPaperBin = mpImplData->mpPrinter->GetPaperBin();
726     mpImplData->mnFixedPaperBin = -1;
727 }
728 
729 void PrinterController:: resetPrinterOptions( bool i_bFileOutput )
730 {
731     PrinterOptions aOpt;
732     aOpt.ReadFromConfig( i_bFileOutput );
733     mpImplData->mpPrinter->SetPrinterOptions( aOpt );
734 }
735 
736 bool PrinterController::setupPrinter( Window* i_pParent )
737 {
738     bool bRet = false;
739     if( mpImplData->mpPrinter.get() )
740     {
741         // get old data
742         Size aPaperSize( mpImplData->mpPrinter->PixelToLogic(
743             mpImplData->mpPrinter->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) );
744         sal_uInt16 nPaperBin = mpImplData->mpPrinter->GetPaperBin();
745 
746         // call driver setup
747         bRet = mpImplData->mpPrinter->Setup( i_pParent );
748         if( bRet )
749         {
750             // was papersize or bin  overridden ? if so we need to take action
751             Size aNewPaperSize( mpImplData->mpPrinter->PixelToLogic(
752                 mpImplData->mpPrinter->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) ) );
753             sal_uInt16 nNewPaperBin = mpImplData->mpPrinter->GetPaperBin();
754             if( aNewPaperSize != aPaperSize || nNewPaperBin != nPaperBin )
755             {
756                 mpImplData->maFixedPageSize = aNewPaperSize;
757                 mpImplData->maPageCache.invalidate();
758                 awt::Size aOverrideSize;
759                 aOverrideSize.Width = aNewPaperSize.Width();
760                 aOverrideSize.Height = aNewPaperSize.Height();
761                 setValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OverridePageSize" ) ),
762                           makeAny( aOverrideSize ) );
763                 mpImplData->mnFixedPaperBin = nNewPaperBin;
764             }
765         }
766     }
767     return bRet;
768 }
769 
770 PrinterController::PageSize vcl::ImplPrinterControllerData::modifyJobSetup( const Sequence< PropertyValue >& i_rProps, bool bNoNUP )
771 {
772     PrinterController::PageSize aPageSize;
773     aPageSize.aSize = mpPrinter->GetPaperSize();
774     awt::Size aSetSize, aIsSize;
775     sal_Int32 nPaperBin = mnDefaultPaperBin;
776     for( sal_Int32 nProperty = 0, nPropertyCount = i_rProps.getLength(); nProperty < nPropertyCount; ++nProperty )
777     {
778         if( i_rProps[ nProperty ].Name.equalsAscii( "PreferredPageSize" ) )
779         {
780             i_rProps[ nProperty ].Value >>= aSetSize;
781         }
782         else if( i_rProps[ nProperty ].Name.equalsAscii( "PageSize" ) )
783         {
784             i_rProps[ nProperty ].Value >>= aIsSize;
785         }
786         else if( i_rProps[ nProperty ].Name.equalsAscii( "PageIncludesNonprintableArea" ) )
787         {
788             sal_Bool bVal = sal_False;
789             i_rProps[ nProperty ].Value >>= bVal;
790             aPageSize.bFullPaper = static_cast<bool>(bVal);
791         }
792         else if( i_rProps[ nProperty ].Name.equalsAscii( "PrinterPaperTray" ) )
793         {
794             sal_Int32 nBin = -1;
795             i_rProps[ nProperty ].Value >>= nBin;
796             if( nBin >= 0 && nBin < mpPrinter->GetPaperBinCount() )
797                 nPaperBin = nBin;
798         }
799     }
800 
801     Size aCurSize( mpPrinter->GetPaperSize() );
802     if( aSetSize.Width && aSetSize.Height )
803     {
804         Size aSetPaperSize( aSetSize.Width, aSetSize.Height );
805         Size aRealPaperSize( getRealPaperSize( aSetPaperSize, bNoNUP ) );
806         if( aRealPaperSize != aCurSize )
807             aIsSize = aSetSize;
808     }
809 
810     if( aIsSize.Width && aIsSize.Height )
811     {
812         aPageSize.aSize.Width() = aIsSize.Width;
813         aPageSize.aSize.Height() = aIsSize.Height;
814 
815         Size aRealPaperSize( getRealPaperSize( aPageSize.aSize, bNoNUP ) );
816         if( aRealPaperSize != aCurSize )
817             mpPrinter->SetPaperSizeUser( aRealPaperSize, ! isFixedPageSize() );
818     }
819 
820     if( nPaperBin != -1 && nPaperBin != mpPrinter->GetPaperBin() )
821         mpPrinter->SetPaperBin( nPaperBin );
822 
823     return aPageSize;
824 }
825 
826 int PrinterController::getPageCountProtected() const
827 {
828     const MapMode aMapMode( MAP_100TH_MM );
829 
830     mpImplData->mpPrinter->Push();
831     mpImplData->mpPrinter->SetMapMode( aMapMode );
832     int nPages = getPageCount();
833     mpImplData->mpPrinter->Pop();
834     return nPages;
835 }
836 
837 Sequence< beans::PropertyValue > PrinterController::getPageParametersProtected( int i_nPage ) const
838 {
839     const MapMode aMapMode( MAP_100TH_MM );
840 
841     mpImplData->mpPrinter->Push();
842     mpImplData->mpPrinter->SetMapMode( aMapMode );
843     Sequence< beans::PropertyValue > aResult( getPageParameters( i_nPage ) );
844     mpImplData->mpPrinter->Pop();
845     return aResult;
846 }
847 
848 PrinterController::PageSize PrinterController::getPageFile( int i_nUnfilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
849 {
850     // update progress if necessary
851     if( mpImplData->mpProgress )
852     {
853         // do nothing if printing is canceled
854         if( mpImplData->mpProgress->isCanceled() )
855             return PrinterController::PageSize();
856         mpImplData->mpProgress->tick();
857         Application::Reschedule( true );
858     }
859 
860     if( i_bMayUseCache )
861     {
862         PrinterController::PageSize aPageSize;
863         if( mpImplData->maPageCache.get( i_nUnfilteredPage, o_rMtf, aPageSize ) )
864         {
865             return aPageSize;
866         }
867     }
868     else
869         mpImplData->maPageCache.invalidate();
870 
871     o_rMtf.Clear();
872 
873     // get page parameters
874     Sequence< PropertyValue > aPageParm( getPageParametersProtected( i_nUnfilteredPage ) );
875     const MapMode aMapMode( MAP_100TH_MM );
876 
877     mpImplData->mpPrinter->Push();
878     mpImplData->mpPrinter->SetMapMode( aMapMode );
879 
880     // modify job setup if necessary
881     PrinterController::PageSize aPageSize = mpImplData->modifyJobSetup( aPageParm, true );
882 
883     o_rMtf.SetPrefSize( aPageSize.aSize );
884     o_rMtf.SetPrefMapMode( aMapMode );
885 
886     mpImplData->mpPrinter->EnableOutput( sal_False );
887 
888     o_rMtf.Record( mpImplData->mpPrinter.get() );
889 
890     printPage( i_nUnfilteredPage );
891 
892     o_rMtf.Stop();
893     o_rMtf.WindStart();
894     mpImplData->mpPrinter->Pop();
895 
896     if( i_bMayUseCache )
897         mpImplData->maPageCache.insert( i_nUnfilteredPage, o_rMtf, aPageSize );
898 
899     // reset "FirstPage" property to false now we've gotten at least our first one
900     mpImplData->mbFirstPage = sal_False;
901 
902     return aPageSize;
903 }
904 
905 static void appendSubPage( GDIMetaFile& o_rMtf, const Rectangle& i_rClipRect, GDIMetaFile& io_rSubPage, bool i_bDrawBorder )
906 {
907     // intersect all clipregion actions with our clip rect
908     io_rSubPage.WindStart();
909     io_rSubPage.Clip( i_rClipRect );
910 
911     // save gstate
912     o_rMtf.AddAction( new MetaPushAction( PUSH_ALL ) );
913 
914     // clip to page rect
915     o_rMtf.AddAction( new MetaClipRegionAction( Region( i_rClipRect ), sal_True ) );
916 
917     // append the subpage
918     io_rSubPage.WindStart();
919     io_rSubPage.Play( o_rMtf );
920 
921     // restore gstate
922     o_rMtf.AddAction( new MetaPopAction() );
923 
924     // draw a border
925     if( i_bDrawBorder )
926     {
927         // save gstate
928         o_rMtf.AddAction( new MetaPushAction( PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_CLIPREGION | PUSH_MAPMODE ) );
929         o_rMtf.AddAction( new MetaMapModeAction( MapMode( MAP_100TH_MM ) ) );
930 
931         Rectangle aBorderRect( i_rClipRect );
932         o_rMtf.AddAction( new MetaLineColorAction( Color( COL_BLACK ), sal_True ) );
933         o_rMtf.AddAction( new MetaFillColorAction( Color( COL_TRANSPARENT ), sal_False ) );
934         o_rMtf.AddAction( new MetaRectAction( aBorderRect ) );
935 
936         // restore gstate
937         o_rMtf.AddAction( new MetaPopAction() );
938     }
939 }
940 
941 PrinterController::PageSize PrinterController::getFilteredPageFile( int i_nFilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
942 {
943     const MultiPageSetup& rMPS( mpImplData->maMultiPage );
944     int nSubPages = rMPS.nRows * rMPS.nColumns;
945     if( nSubPages < 1 )
946         nSubPages = 1;
947 
948     // reverse sheet order
949     if( mpImplData->mbReversePageOrder )
950     {
951         int nDocPages = getFilteredPageCount();
952         i_nFilteredPage = nDocPages - 1 - i_nFilteredPage;
953     }
954 
955     // there is no filtering to be done (and possibly the page size of the
956     // original page is to be set), when N-Up is "neutral" that is there is
957     // only one subpage and the margins are 0
958     if( nSubPages == 1 &&
959         rMPS.nLeftMargin == 0 && rMPS.nRightMargin == 0 &&
960         rMPS.nTopMargin == 0 && rMPS.nBottomMargin == 0 )
961     {
962         PrinterController::PageSize aPageSize = getPageFile( i_nFilteredPage, o_rMtf, i_bMayUseCache );
963         Size aPaperSize = mpImplData->getRealPaperSize( aPageSize.aSize, true );
964         mpImplData->mpPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
965         mpImplData->mpPrinter->SetPaperSizeUser( aPaperSize, ! mpImplData->isFixedPageSize() );
966         if( aPaperSize != aPageSize.aSize )
967         {
968             // user overridden page size, center Metafile
969             o_rMtf.WindStart();
970             long nDX = (aPaperSize.Width() - aPageSize.aSize.Width()) / 2;
971             long nDY = (aPaperSize.Height() - aPageSize.aSize.Height()) / 2;
972             o_rMtf.Move( nDX, nDY, mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() );
973             o_rMtf.WindStart();
974             o_rMtf.SetPrefSize( aPaperSize );
975             aPageSize.aSize = aPaperSize;
976         }
977         return aPageSize;
978     }
979 
980     // set last page property really only on the very last page to be rendered
981     // that is on the last subpage of a NUp run
982     sal_Bool bIsLastPage = mpImplData->mbLastPage;
983     mpImplData->mbLastPage = sal_False;
984 
985     Size aPaperSize( mpImplData->getRealPaperSize( mpImplData->maMultiPage.aPaperSize, false ) );
986 
987     // multi page area: page size minus margins + one time spacing right and down
988     // the added spacing is so each subpage can be calculated including its spacing
989     Size aMPArea( aPaperSize );
990     aMPArea.Width()  -= rMPS.nLeftMargin + rMPS.nRightMargin;
991     aMPArea.Width()  += rMPS.nHorizontalSpacing;
992     aMPArea.Height() -= rMPS.nTopMargin + rMPS.nBottomMargin;
993     aMPArea.Height() += rMPS.nVerticalSpacing;
994 
995     // determine offsets
996     long nAdvX = aMPArea.Width() / rMPS.nColumns;
997     long nAdvY = aMPArea.Height() / rMPS.nRows;
998 
999     // determine size of a "cell" subpage, leave a little space around pages
1000     Size aSubPageSize( nAdvX - rMPS.nHorizontalSpacing, nAdvY - rMPS.nVerticalSpacing );
1001 
1002     o_rMtf.Clear();
1003     o_rMtf.SetPrefSize( aPaperSize );
1004     o_rMtf.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
1005     o_rMtf.AddAction( new MetaMapModeAction( MapMode( MAP_100TH_MM ) ) );
1006 
1007     int nDocPages = getPageCountProtected();
1008     for( int nSubPage = 0; nSubPage < nSubPages; nSubPage++ )
1009     {
1010         // map current sub page to real page
1011         int nPage = (i_nFilteredPage * nSubPages + nSubPage) / rMPS.nRepeat;
1012         if( nSubPage == nSubPages-1 ||
1013             nPage == nDocPages-1 )
1014         {
1015             mpImplData->mbLastPage = bIsLastPage;
1016         }
1017         if( nPage >= 0 && nPage < nDocPages )
1018         {
1019             GDIMetaFile aPageFile;
1020             PrinterController::PageSize aPageSize = getPageFile( nPage, aPageFile, i_bMayUseCache );
1021             if( aPageSize.aSize.Width() && aPageSize.aSize.Height() )
1022             {
1023                 long nCellX = 0, nCellY = 0;
1024                 switch( rMPS.nOrder )
1025                 {
1026                 case PrinterController::LRTB:
1027                     nCellX = (nSubPage % rMPS.nColumns);
1028                     nCellY = (nSubPage / rMPS.nColumns);
1029                     break;
1030                 case PrinterController::TBLR:
1031                     nCellX = (nSubPage / rMPS.nRows);
1032                     nCellY = (nSubPage % rMPS.nRows);
1033                     break;
1034                 case PrinterController::RLTB:
1035                     nCellX = rMPS.nColumns - 1 - (nSubPage % rMPS.nColumns);
1036                     nCellY = (nSubPage / rMPS.nColumns);
1037                     break;
1038                 case PrinterController::TBRL:
1039                     nCellX = rMPS.nColumns - 1 - (nSubPage / rMPS.nRows);
1040                     nCellY = (nSubPage % rMPS.nRows);
1041                     break;
1042                 }
1043                 // scale the metafile down to a sub page size
1044                 double fScaleX = double(aSubPageSize.Width())/double(aPageSize.aSize.Width());
1045                 double fScaleY = double(aSubPageSize.Height())/double(aPageSize.aSize.Height());
1046                 double fScale  = std::min( fScaleX, fScaleY );
1047                 aPageFile.Scale( fScale, fScale );
1048                 aPageFile.WindStart();
1049 
1050                 // move the subpage so it is centered in its "cell"
1051                 long nOffX = (aSubPageSize.Width() - long(double(aPageSize.aSize.Width()) * fScale)) / 2;
1052                 long nOffY = (aSubPageSize.Height() - long(double(aPageSize.aSize.Height()) * fScale)) / 2;
1053                 long nX = rMPS.nLeftMargin + nOffX + nAdvX * nCellX;
1054                 long nY = rMPS.nTopMargin + nOffY + nAdvY * nCellY;
1055                 aPageFile.Move( nX, nY, mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() );
1056                 aPageFile.WindStart();
1057                 // calculate border rectangle
1058                 Rectangle aSubPageRect( Point( nX, nY ),
1059                                         Size( long(double(aPageSize.aSize.Width())*fScale),
1060                                               long(double(aPageSize.aSize.Height())*fScale) ) );
1061 
1062                 // append subpage to page
1063                 appendSubPage( o_rMtf, aSubPageRect, aPageFile, rMPS.bDrawBorder );
1064             }
1065         }
1066     }
1067     o_rMtf.WindStart();
1068 
1069     // subsequent getPageFile calls have changed the paper, reset it to current value
1070     mpImplData->mpPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
1071     mpImplData->mpPrinter->SetPaperSizeUser( aPaperSize, ! mpImplData->isFixedPageSize() );
1072 
1073     return PrinterController::PageSize( aPaperSize, true );
1074 }
1075 
1076 int PrinterController::getFilteredPageCount()
1077 {
1078     int nDiv = mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns;
1079     if( nDiv < 1 )
1080         nDiv = 1;
1081     return (getPageCountProtected() * mpImplData->maMultiPage.nRepeat + (nDiv-1)) / nDiv;
1082 }
1083 
1084 sal_uLong PrinterController::removeTransparencies( GDIMetaFile& i_rIn, GDIMetaFile& o_rOut )
1085 {
1086     sal_uLong nRestoreDrawMode = mpImplData->mpPrinter->GetDrawMode();
1087     sal_Int32 nMaxBmpDPIX = mpImplData->mpPrinter->ImplGetDPIX();
1088     sal_Int32 nMaxBmpDPIY = mpImplData->mpPrinter->ImplGetDPIY();
1089 
1090     const PrinterOptions&   rPrinterOptions = mpImplData->mpPrinter->GetPrinterOptions();
1091 
1092     static const sal_Int32 OPTIMAL_BMP_RESOLUTION = 300;
1093     static const sal_Int32 NORMAL_BMP_RESOLUTION  = 200;
1094 
1095 
1096     if( rPrinterOptions.IsReduceBitmaps() )
1097     {
1098         // calculate maximum resolution for bitmap graphics
1099         if( PRINTER_BITMAP_OPTIMAL == rPrinterOptions.GetReducedBitmapMode() )
1100         {
1101             nMaxBmpDPIX = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1102             nMaxBmpDPIY = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1103         }
1104         else if( PRINTER_BITMAP_NORMAL == rPrinterOptions.GetReducedBitmapMode() )
1105         {
1106             nMaxBmpDPIX = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1107             nMaxBmpDPIY = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1108         }
1109         else
1110         {
1111             nMaxBmpDPIX = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIX );
1112             nMaxBmpDPIY = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIY );
1113         }
1114     }
1115 
1116     // convert to greysacles
1117     if( rPrinterOptions.IsConvertToGreyscales() )
1118     {
1119         mpImplData->mpPrinter->SetDrawMode( mpImplData->mpPrinter->GetDrawMode() |
1120                                             ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
1121                                               DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
1122     }
1123 
1124     // disable transparency output
1125     if( rPrinterOptions.IsReduceTransparency() && ( PRINTER_TRANSPARENCY_NONE == rPrinterOptions.GetReducedTransparencyMode() ) )
1126     {
1127         mpImplData->mpPrinter->SetDrawMode( mpImplData->mpPrinter->GetDrawMode() | DRAWMODE_NOTRANSPARENCY );
1128     }
1129 
1130     Color aBg( COL_TRANSPARENT ); // default: let RemoveTransparenciesFromMetaFile do its own background logic
1131     if( mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns > 1 )
1132     {
1133         // in N-Up printing we have no "page" background operation
1134         // we also have no way to determine the paper color
1135         // so let's go for white, which will kill 99.9% of the real cases
1136         aBg = Color( COL_WHITE );
1137     }
1138     mpImplData->mpPrinter->RemoveTransparenciesFromMetaFile( i_rIn, o_rOut, nMaxBmpDPIX, nMaxBmpDPIY,
1139                                                              rPrinterOptions.IsReduceTransparency(),
1140                                                              rPrinterOptions.GetReducedTransparencyMode() == PRINTER_TRANSPARENCY_AUTO,
1141                                                              rPrinterOptions.IsReduceBitmaps() && rPrinterOptions.IsReducedBitmapIncludesTransparency(),
1142                                                              aBg
1143                                                              );
1144     return nRestoreDrawMode;
1145 }
1146 
1147 void PrinterController::printFilteredPage( int i_nPage )
1148 {
1149     if( mpImplData->meJobState != view::PrintableState_JOB_STARTED )
1150         return;
1151 
1152     GDIMetaFile aPageFile;
1153     PrinterController::PageSize aPageSize = getFilteredPageFile( i_nPage, aPageFile );
1154 
1155     if( mpImplData->mpProgress )
1156     {
1157         // do nothing if printing is canceled
1158         if( mpImplData->mpProgress->isCanceled() )
1159         {
1160             setJobState( view::PrintableState_JOB_ABORTED );
1161             return;
1162         }
1163     }
1164 
1165     // in N-Up printing set the correct page size
1166     mpImplData->mpPrinter->SetMapMode( MAP_100TH_MM );
1167 	// aPageSize was filtered through mpImplData->getRealPaperSize already by getFilteredPageFile()
1168     mpImplData->mpPrinter->SetPaperSizeUser( aPageSize.aSize, ! mpImplData->isFixedPageSize() );
1169     if( mpImplData->mnFixedPaperBin != -1 &&
1170         mpImplData->mpPrinter->GetPaperBin() != mpImplData->mnFixedPaperBin )
1171     {
1172         mpImplData->mpPrinter->SetPaperBin( mpImplData->mnFixedPaperBin );
1173     }
1174 
1175     // if full paper is meant to be used, move the output to accomodate for pageoffset
1176     if( aPageSize.bFullPaper )
1177     {
1178         Point aPageOffset( mpImplData->mpPrinter->GetPageOffset() );
1179         aPageFile.WindStart();
1180         aPageFile.Move( -aPageOffset.X(), -aPageOffset.Y(), mpImplData->mpPrinter->ImplGetDPIX(), mpImplData->mpPrinter->ImplGetDPIY() );
1181     }
1182 
1183     GDIMetaFile aCleanedFile;
1184     sal_uLong nRestoreDrawMode = removeTransparencies( aPageFile, aCleanedFile );
1185 
1186     mpImplData->mpPrinter->EnableOutput( sal_True );
1187 
1188     // actually print the page
1189     mpImplData->mpPrinter->ImplStartPage();
1190 
1191     mpImplData->mpPrinter->Push();
1192     aCleanedFile.WindStart();
1193     aCleanedFile.Play( mpImplData->mpPrinter.get() );
1194     mpImplData->mpPrinter->Pop();
1195 
1196     mpImplData->mpPrinter->ImplEndPage();
1197 
1198     mpImplData->mpPrinter->SetDrawMode( nRestoreDrawMode );
1199 }
1200 
1201 void PrinterController::jobStarted()
1202 {
1203 }
1204 
1205 void PrinterController::jobFinished( view::PrintableState )
1206 {
1207 }
1208 
1209 void PrinterController::abortJob()
1210 {
1211     setJobState( view::PrintableState_JOB_ABORTED );
1212     // applications (well, sw) depend on a page request with "IsLastPage" = true
1213     // to free resources, else they (well, sw) will crash eventually
1214     setLastPage( sal_True );
1215     delete mpImplData->mpProgress;
1216     mpImplData->mpProgress = NULL;
1217     GDIMetaFile aMtf;
1218     getPageFile( 0, aMtf, false );
1219 }
1220 
1221 void PrinterController::setLastPage( sal_Bool i_bLastPage )
1222 {
1223     mpImplData->mbLastPage = i_bLastPage;
1224 }
1225 
1226 void PrinterController::setReversePrint( sal_Bool i_bReverse )
1227 {
1228     mpImplData->mbReversePageOrder = i_bReverse;
1229 }
1230 
1231 bool PrinterController::getReversePrint() const
1232 {
1233     return mpImplData->mbReversePageOrder;
1234 }
1235 
1236 Sequence< PropertyValue > PrinterController::getJobProperties( const Sequence< PropertyValue >& i_rMergeList ) const
1237 {
1238     std::hash_set< rtl::OUString, rtl::OUStringHash > aMergeSet;
1239     size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3;
1240     for( int i = 0; i < i_rMergeList.getLength(); i++ )
1241         aMergeSet.insert( i_rMergeList[i].Name );
1242 
1243     Sequence< PropertyValue > aResult( nResultLen );
1244     for( int i = 0; i < i_rMergeList.getLength(); i++ )
1245         aResult[i] = i_rMergeList[i];
1246     int nCur = i_rMergeList.getLength();
1247     for( size_t i = 0; i < mpImplData->maUIProperties.size(); i++ )
1248     {
1249         if( aMergeSet.find( mpImplData->maUIProperties[i].Name ) == aMergeSet.end() )
1250             aResult[nCur++] = mpImplData->maUIProperties[i];
1251     }
1252     // append IsFirstPage
1253     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) ) ) == aMergeSet.end() )
1254     {
1255         PropertyValue aVal;
1256         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) );
1257         aVal.Value <<= mpImplData->mbFirstPage;
1258         aResult[nCur++] = aVal;
1259     }
1260     // append IsLastPage
1261     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) ) ) == aMergeSet.end() )
1262     {
1263         PropertyValue aVal;
1264         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) );
1265         aVal.Value <<= mpImplData->mbLastPage;
1266         aResult[nCur++] = aVal;
1267     }
1268     // append IsPrinter
1269     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) ) ) == aMergeSet.end() )
1270     {
1271         PropertyValue aVal;
1272         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) );
1273         aVal.Value <<= sal_True;
1274         aResult[nCur++] = aVal;
1275     }
1276     aResult.realloc( nCur );
1277     return aResult;
1278 }
1279 
1280 const Sequence< beans::PropertyValue >& PrinterController::getUIOptions() const
1281 {
1282     return mpImplData->maUIOptions;
1283 }
1284 
1285 beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty )
1286 {
1287     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1288         mpImplData->maPropertyToIndex.find( i_rProperty );
1289     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL;
1290 }
1291 
1292 const beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty ) const
1293 {
1294     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1295         mpImplData->maPropertyToIndex.find( i_rProperty );
1296     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL;
1297 }
1298 
1299 Sequence< beans::PropertyValue > PrinterController::getValues( const Sequence< rtl::OUString >& i_rNames ) const
1300 {
1301     Sequence< beans::PropertyValue > aRet( i_rNames.getLength() );
1302     sal_Int32 nFound = 0;
1303     for( sal_Int32 i = 0; i < i_rNames.getLength(); i++ )
1304     {
1305         const beans::PropertyValue* pVal = getValue( i_rNames[i] );
1306         if( pVal )
1307             aRet[ nFound++ ] = *pVal;
1308     }
1309     aRet.realloc( nFound );
1310     return aRet;
1311 }
1312 
1313 void PrinterController::setValue( const rtl::OUString& i_rName, const Any& i_rValue )
1314 {
1315     beans::PropertyValue aVal;
1316     aVal.Name = i_rName;
1317     aVal.Value = i_rValue;
1318 
1319     setValue( aVal );
1320 }
1321 
1322 void PrinterController::setValue( const beans::PropertyValue& i_rValue )
1323 {
1324     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1325         mpImplData->maPropertyToIndex.find( i_rValue.Name );
1326     if( it != mpImplData->maPropertyToIndex.end() )
1327         mpImplData->maUIProperties[ it->second ] = i_rValue;
1328     else
1329     {
1330         // insert correct index into property map
1331         mpImplData->maPropertyToIndex[ i_rValue.Name ] = mpImplData->maUIProperties.size();
1332         mpImplData->maUIProperties.push_back( i_rValue );
1333         mpImplData->maUIPropertyEnabled.push_back( true );
1334     }
1335 }
1336 
1337 void PrinterController::setUIOptions( const Sequence< beans::PropertyValue >& i_rOptions )
1338 {
1339     DBG_ASSERT( mpImplData->maUIOptions.getLength() == 0, "setUIOptions called twice !" );
1340 
1341     mpImplData->maUIOptions = i_rOptions;
1342 
1343     for( int i = 0; i < i_rOptions.getLength(); i++ )
1344     {
1345         Sequence< beans::PropertyValue > aOptProp;
1346         i_rOptions[i].Value >>= aOptProp;
1347         bool bIsEnabled = true;
1348         bool bHaveProperty = false;
1349         rtl::OUString aPropName;
1350         vcl::ImplPrinterControllerData::ControlDependency aDep;
1351         Sequence< sal_Bool > aChoicesDisabled;
1352         for( int n = 0; n < aOptProp.getLength(); n++ )
1353         {
1354             const beans::PropertyValue& rEntry( aOptProp[ n ] );
1355             if( rEntry.Name.equalsAscii( "Property" ) )
1356             {
1357                 PropertyValue aVal;
1358                 rEntry.Value >>= aVal;
1359                 DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name )
1360                             == mpImplData->maPropertyToIndex.end(), "duplicate property entry" );
1361                 setValue( aVal );
1362                 aPropName = aVal.Name;
1363                 bHaveProperty = true;
1364             }
1365             else if( rEntry.Name.equalsAscii( "Enabled" ) )
1366             {
1367                 sal_Bool bValue = sal_True;
1368                 rEntry.Value >>= bValue;
1369                 bIsEnabled = bValue;
1370             }
1371             else if( rEntry.Name.equalsAscii( "DependsOnName" ) )
1372             {
1373                 rEntry.Value >>= aDep.maDependsOnName;
1374             }
1375             else if( rEntry.Name.equalsAscii( "DependsOnEntry" ) )
1376             {
1377                 rEntry.Value >>= aDep.mnDependsOnEntry;
1378             }
1379             else if( rEntry.Name.equalsAscii( "ChoicesDisabled" ) )
1380             {
1381                 rEntry.Value >>= aChoicesDisabled;
1382             }
1383         }
1384         if( bHaveProperty )
1385         {
1386             vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it =
1387                 mpImplData->maPropertyToIndex.find( aPropName );
1388             // sanity check
1389             if( it != mpImplData->maPropertyToIndex.end() )
1390             {
1391                 mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled;
1392             }
1393             if( aDep.maDependsOnName.getLength() > 0 )
1394                 mpImplData->maControlDependencies[ aPropName ] = aDep;
1395             if( aChoicesDisabled.getLength() > 0 )
1396                 mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled;
1397         }
1398     }
1399 }
1400 
1401 void PrinterController::enableUIOption( const rtl::OUString& i_rProperty, bool i_bEnable )
1402 {
1403     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1404         mpImplData->maPropertyToIndex.find( i_rProperty );
1405     if( it != mpImplData->maPropertyToIndex.end() )
1406     {
1407         // call handler only for actual changes
1408         if( ( mpImplData->maUIPropertyEnabled[ it->second ] && ! i_bEnable ) ||
1409             ( ! mpImplData->maUIPropertyEnabled[ it->second ] && i_bEnable ) )
1410         {
1411             mpImplData->maUIPropertyEnabled[ it->second ] = i_bEnable;
1412             rtl::OUString aPropName( i_rProperty );
1413             mpImplData->maOptionChangeHdl.Call( &aPropName );
1414         }
1415     }
1416 }
1417 
1418 bool PrinterController::isUIOptionEnabled( const rtl::OUString& i_rProperty ) const
1419 {
1420     bool bEnabled = false;
1421     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator prop_it =
1422         mpImplData->maPropertyToIndex.find( i_rProperty );
1423     if( prop_it != mpImplData->maPropertyToIndex.end() )
1424     {
1425         bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second];
1426 
1427         if( bEnabled )
1428         {
1429             // check control dependencies
1430             vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1431                 mpImplData->maControlDependencies.find( i_rProperty );
1432             if( it != mpImplData->maControlDependencies.end() )
1433             {
1434                 // check if the dependency is enabled
1435                 // if the dependency is disabled, we are too
1436                 bEnabled = isUIOptionEnabled( it->second.maDependsOnName );
1437 
1438                 if( bEnabled )
1439                 {
1440                     // does the dependency have the correct value ?
1441                     const com::sun::star::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName );
1442                     OSL_ENSURE( pVal, "unknown property in dependency" );
1443                     if( pVal )
1444                     {
1445                         sal_Int32 nDepVal = 0;
1446                         sal_Bool bDepVal = sal_False;
1447                         if( pVal->Value >>= nDepVal )
1448                         {
1449                             bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1);
1450                         }
1451                         else if( pVal->Value >>= bDepVal )
1452                         {
1453                             // could be a dependency on a checked boolean
1454                             // in this case the dependency is on a non zero for checked value
1455                             bEnabled = (   bDepVal && it->second.mnDependsOnEntry != 0) ||
1456                                        ( ! bDepVal && it->second.mnDependsOnEntry == 0);
1457                         }
1458                         else
1459                         {
1460                             // if the type does not match something is awry
1461                             OSL_ENSURE( 0, "strange type in control dependency" );
1462                             bEnabled = false;
1463                         }
1464                     }
1465                 }
1466             }
1467         }
1468     }
1469     return bEnabled;
1470 }
1471 
1472 bool PrinterController::isUIChoiceEnabled( const rtl::OUString& i_rProperty, sal_Int32 i_nValue ) const
1473 {
1474     bool bEnabled = true;
1475     ImplPrinterControllerData::ChoiceDisableMap::const_iterator it =
1476         mpImplData->maChoiceDisableMap.find( i_rProperty );
1477     if(it != mpImplData->maChoiceDisableMap.end() )
1478     {
1479         const Sequence< sal_Bool >& rDisabled( it->second );
1480         if( i_nValue >= 0 && i_nValue < rDisabled.getLength() )
1481             bEnabled = ! rDisabled[i_nValue];
1482     }
1483     return bEnabled;
1484 }
1485 
1486 rtl::OUString PrinterController::getDependency( const rtl::OUString& i_rProperty ) const
1487 {
1488     rtl::OUString aDependency;
1489 
1490     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1491         mpImplData->maControlDependencies.find( i_rProperty );
1492     if( it != mpImplData->maControlDependencies.end() )
1493         aDependency = it->second.maDependsOnName;
1494 
1495     return aDependency;
1496 }
1497 
1498 rtl::OUString PrinterController::makeEnabled( const rtl::OUString& i_rProperty )
1499 {
1500     rtl::OUString aDependency;
1501 
1502     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1503         mpImplData->maControlDependencies.find( i_rProperty );
1504     if( it != mpImplData->maControlDependencies.end() )
1505     {
1506         if( isUIOptionEnabled( it->second.maDependsOnName ) )
1507         {
1508            aDependency = it->second.maDependsOnName;
1509            const com::sun::star::beans::PropertyValue* pVal = getValue( aDependency );
1510            OSL_ENSURE( pVal, "unknown property in dependency" );
1511            if( pVal )
1512            {
1513                sal_Int32 nDepVal = 0;
1514                sal_Bool bDepVal = sal_False;
1515                if( pVal->Value >>= nDepVal )
1516                {
1517                    if( it->second.mnDependsOnEntry != -1 )
1518                    {
1519                        setValue( aDependency, makeAny( sal_Int32( it->second.mnDependsOnEntry ) ) );
1520                    }
1521                }
1522                else if( pVal->Value >>= bDepVal )
1523                {
1524                    setValue( aDependency, makeAny( sal_Bool( it->second.mnDependsOnEntry != 0 ) ) );
1525                }
1526                else
1527                {
1528                    // if the type does not match something is awry
1529                    OSL_ENSURE( 0, "strange type in control dependency" );
1530                }
1531            }
1532         }
1533     }
1534 
1535     return aDependency;
1536 }
1537 
1538 void PrinterController::setOptionChangeHdl( const Link& i_rHdl )
1539 {
1540     mpImplData->maOptionChangeHdl = i_rHdl;
1541 }
1542 
1543 void PrinterController::createProgressDialog()
1544 {
1545     if( ! mpImplData->mpProgress )
1546     {
1547         sal_Bool bShow = sal_True;
1548         beans::PropertyValue* pMonitor = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MonitorVisible" ) ) );
1549         if( pMonitor )
1550             pMonitor->Value >>= bShow;
1551         else
1552         {
1553             const com::sun::star::beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ) );
1554             if( pVal )
1555             {
1556                 sal_Bool bApi = sal_False;
1557                 pVal->Value >>= bApi;
1558                 bShow = ! bApi;
1559             }
1560         }
1561 
1562         if( bShow && ! Application::IsHeadlessModeEnabled() )
1563         {
1564             mpImplData->mpProgress = new PrintProgressDialog( NULL, getPageCountProtected() );
1565             mpImplData->mpProgress->Show();
1566         }
1567     }
1568     else
1569         mpImplData->mpProgress->reset();
1570 }
1571 
1572 bool PrinterController::isProgressCanceled() const
1573 {
1574     return mpImplData->mpProgress && mpImplData->mpProgress->isCanceled();
1575 }
1576 
1577 void PrinterController::setMultipage( const MultiPageSetup& i_rMPS )
1578 {
1579     mpImplData->maMultiPage = i_rMPS;
1580 }
1581 
1582 const PrinterController::MultiPageSetup& PrinterController::getMultipage() const
1583 {
1584     return mpImplData->maMultiPage;
1585 }
1586 
1587 void PrinterController::pushPropertiesToPrinter()
1588 {
1589     sal_Int32 nCopyCount = 1;
1590     // set copycount and collate
1591     const beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CopyCount" ) ) );
1592     if( pVal )
1593         pVal->Value >>= nCopyCount;
1594     sal_Bool bCollate = sal_False;
1595     pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) );
1596     if( pVal )
1597         pVal->Value >>= bCollate;
1598     mpImplData->mpPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate );
1599 
1600     // duplex mode
1601     pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DuplexMode" ) ) );
1602     if( pVal )
1603     {
1604         sal_Int16 nDuplex = view::DuplexMode::UNKNOWN;
1605         pVal->Value >>= nDuplex;
1606         switch( nDuplex )
1607         {
1608         case view::DuplexMode::OFF: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_OFF ); break;
1609         case view::DuplexMode::LONGEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_LONGEDGE ); break;
1610         case view::DuplexMode::SHORTEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_SHORTEDGE ); break;
1611         }
1612     }
1613 }
1614 
1615 bool PrinterController::isShowDialogs() const
1616 {
1617     sal_Bool bApi = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ), sal_False );
1618     return ! bApi && ! Application::IsHeadlessModeEnabled();
1619 }
1620 
1621 bool PrinterController::isDirectPrint() const
1622 {
1623     sal_Bool bDirect = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDirect" ) ), sal_False );
1624     return bDirect == sal_True;
1625 }
1626 
1627 sal_Bool PrinterController::getBoolProperty( const rtl::OUString& i_rProperty, sal_Bool i_bFallback ) const
1628 {
1629     sal_Bool bRet = i_bFallback;
1630     const com::sun::star::beans::PropertyValue* pVal = getValue( i_rProperty );
1631     if( pVal )
1632         pVal->Value >>= bRet;
1633     return bRet;
1634 }
1635 
1636 /*
1637  * PrinterOptionsHelper
1638 **/
1639 Any PrinterOptionsHelper::getValue( const rtl::OUString& i_rPropertyName ) const
1640 {
1641     Any aRet;
1642     std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it =
1643         m_aPropertyMap.find( i_rPropertyName );
1644     if( it != m_aPropertyMap.end() )
1645         aRet = it->second;
1646     return aRet;
1647 }
1648 
1649 void PrinterOptionsHelper::setValue( const rtl::OUString& i_rPropertyName, const Any& i_rValue )
1650 {
1651     m_aPropertyMap[ i_rPropertyName ] = i_rValue;
1652 }
1653 
1654 bool PrinterOptionsHelper::hasProperty( const rtl::OUString& i_rPropertyName ) const
1655 {
1656     Any aRet;
1657     std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it =
1658         m_aPropertyMap.find( i_rPropertyName );
1659     return it != m_aPropertyMap.end();
1660 }
1661 
1662 sal_Bool PrinterOptionsHelper::getBoolValue( const rtl::OUString& i_rPropertyName, sal_Bool i_bDefault ) const
1663 {
1664     sal_Bool bRet = sal_False;
1665     Any aVal( getValue( i_rPropertyName ) );
1666     return (aVal >>= bRet) ? bRet : i_bDefault;
1667 }
1668 
1669 sal_Int64 PrinterOptionsHelper::getIntValue( const rtl::OUString& i_rPropertyName, sal_Int64 i_nDefault ) const
1670 {
1671     sal_Int64 nRet = 0;
1672     Any aVal( getValue( i_rPropertyName ) );
1673     return (aVal >>= nRet) ? nRet : i_nDefault;
1674 }
1675 
1676 rtl::OUString PrinterOptionsHelper::getStringValue( const rtl::OUString& i_rPropertyName, const rtl::OUString& i_rDefault ) const
1677 {
1678     rtl::OUString aRet;
1679     Any aVal( getValue( i_rPropertyName ) );
1680     return (aVal >>= aRet) ? aRet : i_rDefault;
1681 }
1682 
1683 bool PrinterOptionsHelper::processProperties( const Sequence< PropertyValue >& i_rNewProp,
1684                                               std::set< rtl::OUString >* o_pChangeProp )
1685 {
1686     bool bChanged = false;
1687 
1688     // clear the changed set
1689     if( o_pChangeProp )
1690         o_pChangeProp->clear();
1691 
1692     sal_Int32 nElements = i_rNewProp.getLength();
1693     const PropertyValue* pVals = i_rNewProp.getConstArray();
1694     for( sal_Int32 i = 0; i < nElements; i++ )
1695     {
1696         bool bElementChanged = false;
1697         std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::iterator it =
1698             m_aPropertyMap.find( pVals[ i ].Name );
1699         if( it != m_aPropertyMap.end() )
1700         {
1701             if( it->second != pVals[ i ].Value )
1702                 bElementChanged = true;
1703         }
1704         else
1705             bElementChanged = true;
1706 
1707         if( bElementChanged )
1708         {
1709             if( o_pChangeProp )
1710                 o_pChangeProp->insert( pVals[ i ].Name );
1711             m_aPropertyMap[ pVals[i].Name ] = pVals[i].Value;
1712             bChanged = true;
1713         }
1714     }
1715     return bChanged;
1716 }
1717 
1718 void PrinterOptionsHelper::appendPrintUIOptions( uno::Sequence< beans::PropertyValue >& io_rProps ) const
1719 {
1720     if( m_aUIProperties.getLength() > 0 )
1721     {
1722         sal_Int32 nIndex = io_rProps.getLength();
1723         io_rProps.realloc( nIndex+1 );
1724         PropertyValue aVal;
1725         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExtraPrintUIOptions" ) );
1726         aVal.Value = makeAny( m_aUIProperties );
1727         io_rProps[ nIndex ] = aVal;
1728     }
1729 }
1730 
1731 Any PrinterOptionsHelper::getUIControlOpt( const rtl::OUString& i_rTitle,
1732                                            const Sequence< rtl::OUString >& i_rHelpIds,
1733                                            const rtl::OUString& i_rType,
1734                                            const PropertyValue* i_pVal,
1735                                            const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1736                                            )
1737 {
1738     sal_Int32 nElements =
1739         1                                                               // ControlType
1740         + (i_rTitle.getLength() ? 1 : 0)                                // Text
1741         + (i_rHelpIds.getLength() ? 1 : 0)                              // HelpId
1742         + (i_pVal ? 1 : 0)                                              // Property
1743         + i_rControlOptions.maAddProps.getLength()                      // additional props
1744         + (i_rControlOptions.maGroupHint.getLength() ? 1 : 0)           // grouping
1745         + (i_rControlOptions.mbInternalOnly ? 1 : 0)                    // internal hint
1746         + (i_rControlOptions.mbEnabled ? 0 : 1)                         // enabled
1747         ;
1748     if( i_rControlOptions.maDependsOnName.getLength() )
1749     {
1750         nElements += 1;
1751         if( i_rControlOptions.mnDependsOnEntry != -1 )
1752             nElements += 1;
1753         if( i_rControlOptions.mbAttachToDependency )
1754             nElements += 1;
1755     }
1756 
1757     Sequence< PropertyValue > aCtrl( nElements );
1758     sal_Int32 nUsed = 0;
1759     if( i_rTitle.getLength() )
1760     {
1761         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" ) );
1762         aCtrl[nUsed++].Value = makeAny( i_rTitle );
1763     }
1764     if( i_rHelpIds.getLength() )
1765     {
1766         aCtrl[nUsed  ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpId" ) );
1767         aCtrl[nUsed++].Value = makeAny( i_rHelpIds );
1768     }
1769     aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ControlType" ) );
1770     aCtrl[nUsed++].Value = makeAny( i_rType );
1771     if( i_pVal )
1772     {
1773         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Property" ) );
1774         aCtrl[nUsed++].Value = makeAny( *i_pVal );
1775     }
1776     if( i_rControlOptions.maDependsOnName.getLength() )
1777     {
1778         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnName" ) );
1779         aCtrl[nUsed++].Value = makeAny( i_rControlOptions.maDependsOnName );
1780         if( i_rControlOptions.mnDependsOnEntry != -1 )
1781         {
1782             aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnEntry" ) );
1783             aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mnDependsOnEntry );
1784         }
1785         if( i_rControlOptions.mbAttachToDependency )
1786         {
1787             aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AttachToDependency" ) );
1788             aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mbAttachToDependency );
1789         }
1790     }
1791     if( i_rControlOptions.maGroupHint.getLength() )
1792     {
1793         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GroupingHint" ) );
1794         aCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint;
1795     }
1796     if( i_rControlOptions.mbInternalOnly )
1797     {
1798         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "InternalUIOnly" ) );
1799         aCtrl[nUsed++].Value <<= sal_True;
1800     }
1801     if( ! i_rControlOptions.mbEnabled )
1802     {
1803         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Enabled" ) );
1804         aCtrl[nUsed++].Value <<= sal_False;
1805     }
1806 
1807     sal_Int32 nAddProps = i_rControlOptions.maAddProps.getLength();
1808     for( sal_Int32 i = 0; i < nAddProps; i++ )
1809         aCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i];
1810 
1811     DBG_ASSERT( nUsed == nElements, "nUsed != nElements, probable heap corruption" );
1812 
1813     return makeAny( aCtrl );
1814 }
1815 
1816 Any PrinterOptionsHelper::getGroupControlOpt( const rtl::OUString& i_rTitle, const rtl::OUString& i_rHelpId )
1817 {
1818     Sequence< rtl::OUString > aHelpId;
1819     if( i_rHelpId.getLength() > 0 )
1820     {
1821         aHelpId.realloc( 1 );
1822         *aHelpId.getArray() = i_rHelpId;
1823     }
1824     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Group" ) ) );
1825 }
1826 
1827 Any PrinterOptionsHelper::getSubgroupControlOpt( const rtl::OUString& i_rTitle,
1828                                                  const rtl::OUString& i_rHelpId,
1829                                                  const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1830                                                  )
1831 {
1832     Sequence< rtl::OUString > aHelpId;
1833     if( i_rHelpId.getLength() > 0 )
1834     {
1835         aHelpId.realloc( 1 );
1836         *aHelpId.getArray() = i_rHelpId;
1837     }
1838     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Subgroup" ) ),
1839                             NULL, i_rControlOptions );
1840 }
1841 
1842 Any PrinterOptionsHelper::getBoolControlOpt( const rtl::OUString& i_rTitle,
1843                                              const rtl::OUString& i_rHelpId,
1844                                              const rtl::OUString& i_rProperty,
1845                                              sal_Bool i_bValue,
1846                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1847                                              )
1848 {
1849     Sequence< rtl::OUString > aHelpId;
1850     if( i_rHelpId.getLength() > 0 )
1851     {
1852         aHelpId.realloc( 1 );
1853         *aHelpId.getArray() = i_rHelpId;
1854     }
1855     PropertyValue aVal;
1856     aVal.Name = i_rProperty;
1857     aVal.Value = makeAny( i_bValue );
1858     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Bool" ) ), &aVal, i_rControlOptions );
1859 }
1860 
1861 Any PrinterOptionsHelper::getChoiceControlOpt( const rtl::OUString& i_rTitle,
1862                                                const Sequence< rtl::OUString >& i_rHelpId,
1863                                                const rtl::OUString& i_rProperty,
1864                                                const Sequence< rtl::OUString >& i_rChoices,
1865                                                sal_Int32 i_nValue,
1866                                                const rtl::OUString& i_rType,
1867                                                const Sequence< sal_Bool >& i_rDisabledChoices,
1868                                                const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1869                                                )
1870 {
1871     UIControlOptions aOpt( i_rControlOptions );
1872     sal_Int32 nUsed = aOpt.maAddProps.getLength();
1873     aOpt.maAddProps.realloc( nUsed + 1 + (i_rDisabledChoices.getLength() ? 1 : 0) );
1874     aOpt.maAddProps[nUsed].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Choices" ) );
1875     aOpt.maAddProps[nUsed].Value = makeAny( i_rChoices );
1876     if( i_rDisabledChoices.getLength() )
1877     {
1878         aOpt.maAddProps[nUsed+1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ChoicesDisabled" ) );
1879         aOpt.maAddProps[nUsed+1].Value = makeAny( i_rDisabledChoices );
1880     }
1881 
1882     PropertyValue aVal;
1883     aVal.Name = i_rProperty;
1884     aVal.Value = makeAny( i_nValue );
1885     return getUIControlOpt( i_rTitle, i_rHelpId, i_rType, &aVal, aOpt );
1886 }
1887 
1888 Any PrinterOptionsHelper::getRangeControlOpt( const rtl::OUString& i_rTitle,
1889                                               const rtl::OUString& i_rHelpId,
1890                                               const rtl::OUString& i_rProperty,
1891                                               sal_Int32 i_nValue,
1892                                               sal_Int32 i_nMinValue,
1893                                               sal_Int32 i_nMaxValue,
1894                                               const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1895                                             )
1896 {
1897     UIControlOptions aOpt( i_rControlOptions );
1898     if( i_nMaxValue >= i_nMinValue )
1899     {
1900         sal_Int32 nUsed = aOpt.maAddProps.getLength();
1901         aOpt.maAddProps.realloc( nUsed + 2 );
1902         aOpt.maAddProps[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MinValue" ) );
1903         aOpt.maAddProps[nUsed++].Value = makeAny( i_nMinValue );
1904         aOpt.maAddProps[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MaxValue" ) );
1905         aOpt.maAddProps[nUsed++].Value = makeAny( i_nMaxValue );
1906     }
1907 
1908     Sequence< rtl::OUString > aHelpId;
1909     if( i_rHelpId.getLength() > 0 )
1910     {
1911         aHelpId.realloc( 1 );
1912         *aHelpId.getArray() = i_rHelpId;
1913     }
1914     PropertyValue aVal;
1915     aVal.Name = i_rProperty;
1916     aVal.Value = makeAny( i_nValue );
1917     return getUIControlOpt( i_rTitle,
1918                             aHelpId,
1919                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Range" ) ),
1920                             &aVal,
1921                             aOpt
1922                             );
1923 }
1924 
1925 Any PrinterOptionsHelper::getEditControlOpt( const rtl::OUString& i_rTitle,
1926                                              const rtl::OUString& i_rHelpId,
1927                                              const rtl::OUString& i_rProperty,
1928                                              const rtl::OUString& i_rValue,
1929                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1930                                            )
1931 {
1932     Sequence< rtl::OUString > aHelpId;
1933     if( i_rHelpId.getLength() > 0 )
1934     {
1935         aHelpId.realloc( 1 );
1936         *aHelpId.getArray() = i_rHelpId;
1937     }
1938     PropertyValue aVal;
1939     aVal.Name = i_rProperty;
1940     aVal.Value = makeAny( i_rValue );
1941     return getUIControlOpt( i_rTitle,
1942                             aHelpId,
1943                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Edit" ) ),
1944                             &aVal,
1945                             i_rControlOptions
1946                             );
1947 }
1948