xref: /trunk/main/vcl/source/gdi/print3.cxx (revision 1db32867)
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     setLastPage( sal_False );
1258 }
1259 
1260 void PrinterController::abortJob()
1261 {
1262     setJobState( view::PrintableState_JOB_ABORTED );
1263 
1264     triggerAppToFreeResources();
1265 }
1266 
1267 void PrinterController::setLastPage( sal_Bool i_bLastPage )
1268 {
1269     mpImplData->mbLastPage = i_bLastPage;
1270 }
1271 
1272 void PrinterController::setReversePrint( sal_Bool i_bReverse )
1273 {
1274     mpImplData->mbReversePageOrder = i_bReverse;
1275 }
1276 
1277 bool PrinterController::getReversePrint() const
1278 {
1279     return mpImplData->mbReversePageOrder;
1280 }
1281 
1282 Sequence< PropertyValue > PrinterController::getJobProperties( const Sequence< PropertyValue >& i_rMergeList ) const
1283 {
1284     std::hash_set< rtl::OUString, rtl::OUStringHash > aMergeSet;
1285     size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3;
1286     for( int i = 0; i < i_rMergeList.getLength(); i++ )
1287         aMergeSet.insert( i_rMergeList[i].Name );
1288 
1289     Sequence< PropertyValue > aResult( nResultLen );
1290     for( int i = 0; i < i_rMergeList.getLength(); i++ )
1291         aResult[i] = i_rMergeList[i];
1292     int nCur = i_rMergeList.getLength();
1293     for( size_t i = 0; i < mpImplData->maUIProperties.size(); i++ )
1294     {
1295         if( aMergeSet.find( mpImplData->maUIProperties[i].Name ) == aMergeSet.end() )
1296             aResult[nCur++] = mpImplData->maUIProperties[i];
1297     }
1298     // append IsFirstPage
1299     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) ) ) == aMergeSet.end() )
1300     {
1301         PropertyValue aVal;
1302         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFirstPage" ) );
1303         aVal.Value <<= mpImplData->mbFirstPage;
1304         aResult[nCur++] = aVal;
1305     }
1306     // append IsLastPage
1307     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) ) ) == aMergeSet.end() )
1308     {
1309         PropertyValue aVal;
1310         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsLastPage" ) );
1311         aVal.Value <<= mpImplData->mbLastPage;
1312         aResult[nCur++] = aVal;
1313     }
1314     // append IsPrinter
1315     if( aMergeSet.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) ) ) == aMergeSet.end() )
1316     {
1317         PropertyValue aVal;
1318         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsPrinter" ) );
1319         aVal.Value <<= sal_True;
1320         aResult[nCur++] = aVal;
1321     }
1322     aResult.realloc( nCur );
1323     return aResult;
1324 }
1325 
1326 const Sequence< beans::PropertyValue >& PrinterController::getUIOptions() const
1327 {
1328     return mpImplData->maUIOptions;
1329 }
1330 
1331 beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty )
1332 {
1333     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1334         mpImplData->maPropertyToIndex.find( i_rProperty );
1335     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL;
1336 }
1337 
1338 const beans::PropertyValue* PrinterController::getValue( const rtl::OUString& i_rProperty ) const
1339 {
1340     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1341         mpImplData->maPropertyToIndex.find( i_rProperty );
1342     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : NULL;
1343 }
1344 
1345 Sequence< beans::PropertyValue > PrinterController::getValues( const Sequence< rtl::OUString >& i_rNames ) const
1346 {
1347     Sequence< beans::PropertyValue > aRet( i_rNames.getLength() );
1348     sal_Int32 nFound = 0;
1349     for( sal_Int32 i = 0; i < i_rNames.getLength(); i++ )
1350     {
1351         const beans::PropertyValue* pVal = getValue( i_rNames[i] );
1352         if( pVal )
1353             aRet[ nFound++ ] = *pVal;
1354     }
1355     aRet.realloc( nFound );
1356     return aRet;
1357 }
1358 
1359 void PrinterController::setValue( const rtl::OUString& i_rName, const Any& i_rValue )
1360 {
1361     beans::PropertyValue aVal;
1362     aVal.Name = i_rName;
1363     aVal.Value = i_rValue;
1364 
1365     setValue( aVal );
1366 }
1367 
1368 void PrinterController::setValue( const beans::PropertyValue& i_rValue )
1369 {
1370     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1371         mpImplData->maPropertyToIndex.find( i_rValue.Name );
1372     if( it != mpImplData->maPropertyToIndex.end() )
1373         mpImplData->maUIProperties[ it->second ] = i_rValue;
1374     else
1375     {
1376         // insert correct index into property map
1377         mpImplData->maPropertyToIndex[ i_rValue.Name ] = mpImplData->maUIProperties.size();
1378         mpImplData->maUIProperties.push_back( i_rValue );
1379         mpImplData->maUIPropertyEnabled.push_back( true );
1380     }
1381 }
1382 
1383 void PrinterController::setUIOptions( const Sequence< beans::PropertyValue >& i_rOptions )
1384 {
1385     DBG_ASSERT( mpImplData->maUIOptions.getLength() == 0, "setUIOptions called twice !" );
1386 
1387     mpImplData->maUIOptions = i_rOptions;
1388 
1389     for( int i = 0; i < i_rOptions.getLength(); i++ )
1390     {
1391         Sequence< beans::PropertyValue > aOptProp;
1392         i_rOptions[i].Value >>= aOptProp;
1393         bool bIsEnabled = true;
1394         bool bHaveProperty = false;
1395         rtl::OUString aPropName;
1396         vcl::ImplPrinterControllerData::ControlDependency aDep;
1397         Sequence< sal_Bool > aChoicesDisabled;
1398         for( int n = 0; n < aOptProp.getLength(); n++ )
1399         {
1400             const beans::PropertyValue& rEntry( aOptProp[ n ] );
1401             if( rEntry.Name.equalsAscii( "Property" ) )
1402             {
1403                 PropertyValue aVal;
1404                 rEntry.Value >>= aVal;
1405                 DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name )
1406                             == mpImplData->maPropertyToIndex.end(), "duplicate property entry" );
1407                 setValue( aVal );
1408                 aPropName = aVal.Name;
1409                 bHaveProperty = true;
1410             }
1411             else if( rEntry.Name.equalsAscii( "Enabled" ) )
1412             {
1413                 sal_Bool bValue = sal_True;
1414                 rEntry.Value >>= bValue;
1415                 bIsEnabled = bValue;
1416             }
1417             else if( rEntry.Name.equalsAscii( "DependsOnName" ) )
1418             {
1419                 rEntry.Value >>= aDep.maDependsOnName;
1420             }
1421             else if( rEntry.Name.equalsAscii( "DependsOnEntry" ) )
1422             {
1423                 rEntry.Value >>= aDep.mnDependsOnEntry;
1424             }
1425             else if( rEntry.Name.equalsAscii( "ChoicesDisabled" ) )
1426             {
1427                 rEntry.Value >>= aChoicesDisabled;
1428             }
1429         }
1430         if( bHaveProperty )
1431         {
1432             vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it =
1433                 mpImplData->maPropertyToIndex.find( aPropName );
1434             // sanity check
1435             if( it != mpImplData->maPropertyToIndex.end() )
1436             {
1437                 mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled;
1438             }
1439             if( aDep.maDependsOnName.getLength() > 0 )
1440                 mpImplData->maControlDependencies[ aPropName ] = aDep;
1441             if( aChoicesDisabled.getLength() > 0 )
1442                 mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled;
1443         }
1444     }
1445 }
1446 
1447 void PrinterController::enableUIOption( const rtl::OUString& i_rProperty, bool i_bEnable )
1448 {
1449     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator it =
1450         mpImplData->maPropertyToIndex.find( i_rProperty );
1451     if( it != mpImplData->maPropertyToIndex.end() )
1452     {
1453         // call handler only for actual changes
1454         if( ( mpImplData->maUIPropertyEnabled[ it->second ] && ! i_bEnable ) ||
1455             ( ! mpImplData->maUIPropertyEnabled[ it->second ] && i_bEnable ) )
1456         {
1457             mpImplData->maUIPropertyEnabled[ it->second ] = i_bEnable;
1458             rtl::OUString aPropName( i_rProperty );
1459             mpImplData->maOptionChangeHdl.Call( &aPropName );
1460         }
1461     }
1462 }
1463 
1464 bool PrinterController::isUIOptionEnabled( const rtl::OUString& i_rProperty ) const
1465 {
1466     bool bEnabled = false;
1467     std::hash_map< rtl::OUString, size_t, rtl::OUStringHash >::const_iterator prop_it =
1468         mpImplData->maPropertyToIndex.find( i_rProperty );
1469     if( prop_it != mpImplData->maPropertyToIndex.end() )
1470     {
1471         bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second];
1472 
1473         if( bEnabled )
1474         {
1475             // check control dependencies
1476             vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1477                 mpImplData->maControlDependencies.find( i_rProperty );
1478             if( it != mpImplData->maControlDependencies.end() )
1479             {
1480                 // check if the dependency is enabled
1481                 // if the dependency is disabled, we are too
1482                 bEnabled = isUIOptionEnabled( it->second.maDependsOnName );
1483 
1484                 if( bEnabled )
1485                 {
1486                     // does the dependency have the correct value ?
1487                     const com::sun::star::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName );
1488                     OSL_ENSURE( pVal, "unknown property in dependency" );
1489                     if( pVal )
1490                     {
1491                         sal_Int32 nDepVal = 0;
1492                         sal_Bool bDepVal = sal_False;
1493                         if( pVal->Value >>= nDepVal )
1494                         {
1495                             bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1);
1496                         }
1497                         else if( pVal->Value >>= bDepVal )
1498                         {
1499                             // could be a dependency on a checked boolean
1500                             // in this case the dependency is on a non zero for checked value
1501                             bEnabled = (   bDepVal && it->second.mnDependsOnEntry != 0) ||
1502                                        ( ! bDepVal && it->second.mnDependsOnEntry == 0);
1503                         }
1504                         else
1505                         {
1506                             // if the type does not match something is awry
1507                             OSL_ENSURE( 0, "strange type in control dependency" );
1508                             bEnabled = false;
1509                         }
1510                     }
1511                 }
1512             }
1513         }
1514     }
1515     return bEnabled;
1516 }
1517 
1518 bool PrinterController::isUIChoiceEnabled( const rtl::OUString& i_rProperty, sal_Int32 i_nValue ) const
1519 {
1520     bool bEnabled = true;
1521     ImplPrinterControllerData::ChoiceDisableMap::const_iterator it =
1522         mpImplData->maChoiceDisableMap.find( i_rProperty );
1523     if(it != mpImplData->maChoiceDisableMap.end() )
1524     {
1525         const Sequence< sal_Bool >& rDisabled( it->second );
1526         if( i_nValue >= 0 && i_nValue < rDisabled.getLength() )
1527             bEnabled = ! rDisabled[i_nValue];
1528     }
1529     return bEnabled;
1530 }
1531 
1532 rtl::OUString PrinterController::getDependency( const rtl::OUString& i_rProperty ) const
1533 {
1534     rtl::OUString aDependency;
1535 
1536     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1537         mpImplData->maControlDependencies.find( i_rProperty );
1538     if( it != mpImplData->maControlDependencies.end() )
1539         aDependency = it->second.maDependsOnName;
1540 
1541     return aDependency;
1542 }
1543 
1544 rtl::OUString PrinterController::makeEnabled( const rtl::OUString& i_rProperty )
1545 {
1546     rtl::OUString aDependency;
1547 
1548     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1549         mpImplData->maControlDependencies.find( i_rProperty );
1550     if( it != mpImplData->maControlDependencies.end() )
1551     {
1552         if( isUIOptionEnabled( it->second.maDependsOnName ) )
1553         {
1554            aDependency = it->second.maDependsOnName;
1555            const com::sun::star::beans::PropertyValue* pVal = getValue( aDependency );
1556            OSL_ENSURE( pVal, "unknown property in dependency" );
1557            if( pVal )
1558            {
1559                sal_Int32 nDepVal = 0;
1560                sal_Bool bDepVal = sal_False;
1561                if( pVal->Value >>= nDepVal )
1562                {
1563                    if( it->second.mnDependsOnEntry != -1 )
1564                    {
1565                        setValue( aDependency, makeAny( sal_Int32( it->second.mnDependsOnEntry ) ) );
1566                    }
1567                }
1568                else if( pVal->Value >>= bDepVal )
1569                {
1570                    setValue( aDependency, makeAny( sal_Bool( it->second.mnDependsOnEntry != 0 ) ) );
1571                }
1572                else
1573                {
1574                    // if the type does not match something is awry
1575                    OSL_ENSURE( 0, "strange type in control dependency" );
1576                }
1577            }
1578         }
1579     }
1580 
1581     return aDependency;
1582 }
1583 
1584 void PrinterController::setOptionChangeHdl( const Link& i_rHdl )
1585 {
1586     mpImplData->maOptionChangeHdl = i_rHdl;
1587 }
1588 
1589 void PrinterController::createProgressDialog()
1590 {
1591     if( ! mpImplData->mpProgress )
1592     {
1593         sal_Bool bShow = sal_True;
1594         beans::PropertyValue* pMonitor = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MonitorVisible" ) ) );
1595         if( pMonitor )
1596             pMonitor->Value >>= bShow;
1597         else
1598         {
1599             const com::sun::star::beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ) );
1600             if( pVal )
1601             {
1602                 sal_Bool bApi = sal_False;
1603                 pVal->Value >>= bApi;
1604                 bShow = ! bApi;
1605             }
1606         }
1607 
1608         if( bShow && ! Application::IsHeadlessModeEnabled() )
1609         {
1610             mpImplData->mpProgress = new PrintProgressDialog( NULL, getPageCountProtected() );
1611             mpImplData->mpProgress->Show();
1612         }
1613     }
1614     else
1615         mpImplData->mpProgress->reset();
1616 }
1617 
1618 bool PrinterController::isProgressCanceled() const
1619 {
1620     return mpImplData->mpProgress && mpImplData->mpProgress->isCanceled();
1621 }
1622 
1623 void PrinterController::setMultipage( const MultiPageSetup& i_rMPS )
1624 {
1625     mpImplData->maMultiPage = i_rMPS;
1626 }
1627 
1628 const PrinterController::MultiPageSetup& PrinterController::getMultipage() const
1629 {
1630     return mpImplData->maMultiPage;
1631 }
1632 
1633 void PrinterController::pushPropertiesToPrinter()
1634 {
1635     sal_Int32 nCopyCount = 1;
1636     // set copycount and collate
1637     const beans::PropertyValue* pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CopyCount" ) ) );
1638     if( pVal )
1639         pVal->Value >>= nCopyCount;
1640     sal_Bool bCollate = sal_False;
1641     pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) );
1642     if( pVal )
1643         pVal->Value >>= bCollate;
1644     mpImplData->mpPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate );
1645 
1646     // duplex mode
1647     pVal = getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DuplexMode" ) ) );
1648     if( pVal )
1649     {
1650         sal_Int16 nDuplex = view::DuplexMode::UNKNOWN;
1651         pVal->Value >>= nDuplex;
1652         switch( nDuplex )
1653         {
1654         case view::DuplexMode::OFF: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_OFF ); break;
1655         case view::DuplexMode::LONGEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_LONGEDGE ); break;
1656         case view::DuplexMode::SHORTEDGE: mpImplData->mpPrinter->SetDuplexMode( DUPLEX_SHORTEDGE ); break;
1657         }
1658     }
1659 }
1660 
1661 bool PrinterController::isShowDialogs() const
1662 {
1663     sal_Bool bApi = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsApi" ) ), sal_False );
1664     return ! bApi && ! Application::IsHeadlessModeEnabled();
1665 }
1666 
1667 bool PrinterController::isDirectPrint() const
1668 {
1669     sal_Bool bDirect = getBoolProperty( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDirect" ) ), sal_False );
1670     return bDirect == sal_True;
1671 }
1672 
1673 sal_Bool PrinterController::getBoolProperty( const rtl::OUString& i_rProperty, sal_Bool i_bFallback ) const
1674 {
1675     sal_Bool bRet = i_bFallback;
1676     const com::sun::star::beans::PropertyValue* pVal = getValue( i_rProperty );
1677     if( pVal )
1678         pVal->Value >>= bRet;
1679     return bRet;
1680 }
1681 
1682 sal_Int32 PrinterController::getIntProperty( const rtl::OUString& i_rProperty, sal_Int32 i_nFallback ) const
1683 {
1684     sal_Int32 nRet = i_nFallback;
1685     const com::sun::star::beans::PropertyValue* pVal = getValue( i_rProperty );
1686     if( pVal )
1687         pVal->Value >>= nRet;
1688     return nRet;
1689 }
1690 
1691 /*
1692  * PrinterOptionsHelper
1693 **/
1694 Any PrinterOptionsHelper::getValue( const rtl::OUString& i_rPropertyName ) const
1695 {
1696     Any aRet;
1697     std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it =
1698         m_aPropertyMap.find( i_rPropertyName );
1699     if( it != m_aPropertyMap.end() )
1700         aRet = it->second;
1701     return aRet;
1702 }
1703 
1704 void PrinterOptionsHelper::setValue( const rtl::OUString& i_rPropertyName, const Any& i_rValue )
1705 {
1706     m_aPropertyMap[ i_rPropertyName ] = i_rValue;
1707 }
1708 
1709 bool PrinterOptionsHelper::hasProperty( const rtl::OUString& i_rPropertyName ) const
1710 {
1711     Any aRet;
1712     std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::const_iterator it =
1713         m_aPropertyMap.find( i_rPropertyName );
1714     return it != m_aPropertyMap.end();
1715 }
1716 
1717 sal_Bool PrinterOptionsHelper::getBoolValue( const rtl::OUString& i_rPropertyName, sal_Bool i_bDefault ) const
1718 {
1719     sal_Bool bRet = sal_False;
1720     Any aVal( getValue( i_rPropertyName ) );
1721     return (aVal >>= bRet) ? bRet : i_bDefault;
1722 }
1723 
1724 sal_Int64 PrinterOptionsHelper::getIntValue( const rtl::OUString& i_rPropertyName, sal_Int64 i_nDefault ) const
1725 {
1726     sal_Int64 nRet = 0;
1727     Any aVal( getValue( i_rPropertyName ) );
1728     return (aVal >>= nRet) ? nRet : i_nDefault;
1729 }
1730 
1731 rtl::OUString PrinterOptionsHelper::getStringValue( const rtl::OUString& i_rPropertyName, const rtl::OUString& i_rDefault ) const
1732 {
1733     rtl::OUString aRet;
1734     Any aVal( getValue( i_rPropertyName ) );
1735     return (aVal >>= aRet) ? aRet : i_rDefault;
1736 }
1737 
1738 bool PrinterOptionsHelper::processProperties( const Sequence< PropertyValue >& i_rNewProp,
1739                                               std::set< rtl::OUString >* o_pChangeProp )
1740 {
1741     bool bChanged = false;
1742 
1743     // clear the changed set
1744     if( o_pChangeProp )
1745         o_pChangeProp->clear();
1746 
1747     sal_Int32 nElements = i_rNewProp.getLength();
1748     const PropertyValue* pVals = i_rNewProp.getConstArray();
1749     for( sal_Int32 i = 0; i < nElements; i++ )
1750     {
1751         bool bElementChanged = false;
1752         std::hash_map< rtl::OUString, Any, rtl::OUStringHash >::iterator it =
1753             m_aPropertyMap.find( pVals[ i ].Name );
1754         if( it != m_aPropertyMap.end() )
1755         {
1756             if( it->second != pVals[ i ].Value )
1757                 bElementChanged = true;
1758         }
1759         else
1760             bElementChanged = true;
1761 
1762         if( bElementChanged )
1763         {
1764             if( o_pChangeProp )
1765                 o_pChangeProp->insert( pVals[ i ].Name );
1766             m_aPropertyMap[ pVals[i].Name ] = pVals[i].Value;
1767             bChanged = true;
1768         }
1769     }
1770     return bChanged;
1771 }
1772 
1773 void PrinterOptionsHelper::appendPrintUIOptions( uno::Sequence< beans::PropertyValue >& io_rProps ) const
1774 {
1775     if( m_aUIProperties.getLength() > 0 )
1776     {
1777         sal_Int32 nIndex = io_rProps.getLength();
1778         io_rProps.realloc( nIndex+1 );
1779         PropertyValue aVal;
1780         aVal.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ExtraPrintUIOptions" ) );
1781         aVal.Value = makeAny( m_aUIProperties );
1782         io_rProps[ nIndex ] = aVal;
1783     }
1784 }
1785 
1786 Any PrinterOptionsHelper::getUIControlOpt( const rtl::OUString& i_rTitle,
1787                                            const Sequence< rtl::OUString >& i_rHelpIds,
1788                                            const rtl::OUString& i_rType,
1789                                            const PropertyValue* i_pVal,
1790                                            const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1791                                            )
1792 {
1793     sal_Int32 nElements =
1794         1                                                               // ControlType
1795         + (i_rTitle.getLength() ? 1 : 0)                                // Text
1796         + (i_rHelpIds.getLength() ? 1 : 0)                              // HelpId
1797         + (i_pVal ? 1 : 0)                                              // Property
1798         + i_rControlOptions.maAddProps.getLength()                      // additional props
1799         + (i_rControlOptions.maGroupHint.getLength() ? 1 : 0)           // grouping
1800         + (i_rControlOptions.mbInternalOnly ? 1 : 0)                    // internal hint
1801         + (i_rControlOptions.mbEnabled ? 0 : 1)                         // enabled
1802         ;
1803     if( i_rControlOptions.maDependsOnName.getLength() )
1804     {
1805         nElements += 1;
1806         if( i_rControlOptions.mnDependsOnEntry != -1 )
1807             nElements += 1;
1808         if( i_rControlOptions.mbAttachToDependency )
1809             nElements += 1;
1810     }
1811 
1812     Sequence< PropertyValue > aCtrl( nElements );
1813     sal_Int32 nUsed = 0;
1814     if( i_rTitle.getLength() )
1815     {
1816         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Text" ) );
1817         aCtrl[nUsed++].Value = makeAny( i_rTitle );
1818     }
1819     if( i_rHelpIds.getLength() )
1820     {
1821         aCtrl[nUsed  ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "HelpId" ) );
1822         aCtrl[nUsed++].Value = makeAny( i_rHelpIds );
1823     }
1824     aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ControlType" ) );
1825     aCtrl[nUsed++].Value = makeAny( i_rType );
1826     if( i_pVal )
1827     {
1828         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Property" ) );
1829         aCtrl[nUsed++].Value = makeAny( *i_pVal );
1830     }
1831     if( i_rControlOptions.maDependsOnName.getLength() )
1832     {
1833         aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnName" ) );
1834         aCtrl[nUsed++].Value = makeAny( i_rControlOptions.maDependsOnName );
1835         if( i_rControlOptions.mnDependsOnEntry != -1 )
1836         {
1837             aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DependsOnEntry" ) );
1838             aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mnDependsOnEntry );
1839         }
1840         if( i_rControlOptions.mbAttachToDependency )
1841         {
1842             aCtrl[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AttachToDependency" ) );
1843             aCtrl[nUsed++].Value = makeAny( i_rControlOptions.mbAttachToDependency );
1844         }
1845     }
1846     if( i_rControlOptions.maGroupHint.getLength() )
1847     {
1848         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "GroupingHint" ) );
1849         aCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint;
1850     }
1851     if( i_rControlOptions.mbInternalOnly )
1852     {
1853         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "InternalUIOnly" ) );
1854         aCtrl[nUsed++].Value <<= sal_True;
1855     }
1856     if( ! i_rControlOptions.mbEnabled )
1857     {
1858         aCtrl[nUsed  ].Name    = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Enabled" ) );
1859         aCtrl[nUsed++].Value <<= sal_False;
1860     }
1861 
1862     sal_Int32 nAddProps = i_rControlOptions.maAddProps.getLength();
1863     for( sal_Int32 i = 0; i < nAddProps; i++ )
1864         aCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i];
1865 
1866     DBG_ASSERT( nUsed == nElements, "nUsed != nElements, probable heap corruption" );
1867 
1868     return makeAny( aCtrl );
1869 }
1870 
1871 Any PrinterOptionsHelper::getGroupControlOpt( const rtl::OUString& i_rTitle, const rtl::OUString& i_rHelpId )
1872 {
1873     Sequence< rtl::OUString > aHelpId;
1874     if( i_rHelpId.getLength() > 0 )
1875     {
1876         aHelpId.realloc( 1 );
1877         *aHelpId.getArray() = i_rHelpId;
1878     }
1879     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Group" ) ) );
1880 }
1881 
1882 Any PrinterOptionsHelper::getSubgroupControlOpt( const rtl::OUString& i_rTitle,
1883                                                  const rtl::OUString& i_rHelpId,
1884                                                  const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1885                                                  )
1886 {
1887     Sequence< rtl::OUString > aHelpId;
1888     if( i_rHelpId.getLength() > 0 )
1889     {
1890         aHelpId.realloc( 1 );
1891         *aHelpId.getArray() = i_rHelpId;
1892     }
1893     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Subgroup" ) ),
1894                             NULL, i_rControlOptions );
1895 }
1896 
1897 Any PrinterOptionsHelper::getBoolControlOpt( const rtl::OUString& i_rTitle,
1898                                              const rtl::OUString& i_rHelpId,
1899                                              const rtl::OUString& i_rProperty,
1900                                              sal_Bool i_bValue,
1901                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1902                                              )
1903 {
1904     Sequence< rtl::OUString > aHelpId;
1905     if( i_rHelpId.getLength() > 0 )
1906     {
1907         aHelpId.realloc( 1 );
1908         *aHelpId.getArray() = i_rHelpId;
1909     }
1910     PropertyValue aVal;
1911     aVal.Name = i_rProperty;
1912     aVal.Value = makeAny( i_bValue );
1913     return getUIControlOpt( i_rTitle, aHelpId, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Bool" ) ), &aVal, i_rControlOptions );
1914 }
1915 
1916 Any PrinterOptionsHelper::getChoiceControlOpt( const rtl::OUString& i_rTitle,
1917                                                const Sequence< rtl::OUString >& i_rHelpId,
1918                                                const rtl::OUString& i_rProperty,
1919                                                const Sequence< rtl::OUString >& i_rChoices,
1920                                                sal_Int32 i_nValue,
1921                                                const rtl::OUString& i_rType,
1922                                                const Sequence< sal_Bool >& i_rDisabledChoices,
1923                                                const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1924                                                )
1925 {
1926     UIControlOptions aOpt( i_rControlOptions );
1927     sal_Int32 nUsed = aOpt.maAddProps.getLength();
1928     aOpt.maAddProps.realloc( nUsed + 1 + (i_rDisabledChoices.getLength() ? 1 : 0) );
1929     aOpt.maAddProps[nUsed].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Choices" ) );
1930     aOpt.maAddProps[nUsed].Value = makeAny( i_rChoices );
1931     if( i_rDisabledChoices.getLength() )
1932     {
1933         aOpt.maAddProps[nUsed+1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ChoicesDisabled" ) );
1934         aOpt.maAddProps[nUsed+1].Value = makeAny( i_rDisabledChoices );
1935     }
1936 
1937     PropertyValue aVal;
1938     aVal.Name = i_rProperty;
1939     aVal.Value = makeAny( i_nValue );
1940     return getUIControlOpt( i_rTitle, i_rHelpId, i_rType, &aVal, aOpt );
1941 }
1942 
1943 Any PrinterOptionsHelper::getRangeControlOpt( const rtl::OUString& i_rTitle,
1944                                               const rtl::OUString& i_rHelpId,
1945                                               const rtl::OUString& i_rProperty,
1946                                               sal_Int32 i_nValue,
1947                                               sal_Int32 i_nMinValue,
1948                                               sal_Int32 i_nMaxValue,
1949                                               const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1950                                             )
1951 {
1952     UIControlOptions aOpt( i_rControlOptions );
1953     if( i_nMaxValue >= i_nMinValue )
1954     {
1955         sal_Int32 nUsed = aOpt.maAddProps.getLength();
1956         aOpt.maAddProps.realloc( nUsed + 2 );
1957         aOpt.maAddProps[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MinValue" ) );
1958         aOpt.maAddProps[nUsed++].Value = makeAny( i_nMinValue );
1959         aOpt.maAddProps[nUsed  ].Name  = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MaxValue" ) );
1960         aOpt.maAddProps[nUsed++].Value = makeAny( i_nMaxValue );
1961     }
1962 
1963     Sequence< rtl::OUString > aHelpId;
1964     if( i_rHelpId.getLength() > 0 )
1965     {
1966         aHelpId.realloc( 1 );
1967         *aHelpId.getArray() = i_rHelpId;
1968     }
1969     PropertyValue aVal;
1970     aVal.Name = i_rProperty;
1971     aVal.Value = makeAny( i_nValue );
1972     return getUIControlOpt( i_rTitle,
1973                             aHelpId,
1974                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Range" ) ),
1975                             &aVal,
1976                             aOpt
1977                             );
1978 }
1979 
1980 Any PrinterOptionsHelper::getEditControlOpt( const rtl::OUString& i_rTitle,
1981                                              const rtl::OUString& i_rHelpId,
1982                                              const rtl::OUString& i_rProperty,
1983                                              const rtl::OUString& i_rValue,
1984                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions
1985                                            )
1986 {
1987     Sequence< rtl::OUString > aHelpId;
1988     if( i_rHelpId.getLength() > 0 )
1989     {
1990         aHelpId.realloc( 1 );
1991         *aHelpId.getArray() = i_rHelpId;
1992     }
1993     PropertyValue aVal;
1994     aVal.Name = i_rProperty;
1995     aVal.Value = makeAny( i_rValue );
1996     return getUIControlOpt( i_rTitle,
1997                             aHelpId,
1998                             rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Edit" ) ),
1999                             &aVal,
2000                             i_rControlOptions
2001                             );
2002 }
2003