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