1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
21 #include "printhelper.hxx"
22 
23 #include <com/sun/star/view/XPrintJob.hpp>
24 #include <com/sun/star/awt/Size.hpp>
25 #include <com/sun/star/lang/IllegalArgumentException.hpp>
26 #include <com/sun/star/view/PaperFormat.hpp>
27 #include <com/sun/star/view/PaperOrientation.hpp>
28 #include <com/sun/star/ucb/NameClash.hpp>
29 #include <com/sun/star/ucb/ContentCreationException.hpp>
30 #include <com/sun/star/ucb/CommandAbortedException.hpp>
31 #include <com/sun/star/lang/XUnoTunnel.hpp>
32 #include <com/sun/star/frame/XModel.hpp>
33 #include <com/sun/star/lang/EventObject.hpp>
34 #include <com/sun/star/view/DuplexMode.hpp>
35 #include <comphelper/processfactory.hxx>
36 #include <svl/lstner.hxx>
37 #include <svl/stritem.hxx>
38 #include <svl/intitem.hxx>
39 #include <svl/eitem.hxx>
40 #include <unotools/tempfile.hxx>
41 #include <osl/file.hxx>
42 #include <osl/thread.hxx>
43 #include <tools/globname.hxx>
44 #include <tools/urlobj.hxx>
45 #include <ucbhelper/content.hxx>
46 #include <cppuhelper/interfacecontainer.hxx>
47 #include <osl/mutex.hxx>
48 #include <cppuhelper/implbase.hxx>
49 #include <vcl/settings.hxx>
50 #include <vcl/svapp.hxx>
51 
52 #include <sfx2/viewfrm.hxx>
53 #include <sfx2/viewsh.hxx>
54 #include <sfx2/dispatch.hxx>
55 #include <sfx2/request.hxx>
56 #include <sfx2/printer.hxx>
57 #include <sfx2/app.hxx>
58 #include <sfx2/objsh.hxx>
59 #include <sfx2/event.hxx>
60 
61 #define SFX_PRINTABLESTATE_CANCELJOB    css::view::PrintableState(-2)
62 
63 using namespace ::com::sun::star;
64 using namespace ::com::sun::star::uno;
65 
66 struct IMPL_PrintListener_DataContainer : public SfxListener
67 {
68     SfxObjectShellRef                               m_pObjectShell;
69     ::cppu::OMultiTypeInterfaceContainerHelper      m_aInterfaceContainer;
70     uno::Reference< css::view::XPrintJob>           m_xPrintJob;
71     css::uno::Sequence< css::beans::PropertyValue > m_aPrintOptions;
72 
IMPL_PrintListener_DataContainerIMPL_PrintListener_DataContainer73     explicit IMPL_PrintListener_DataContainer( ::osl::Mutex& aMutex)
74             :   m_aInterfaceContainer   ( aMutex )
75     {
76     }
77 
78 
79     void Notify(            SfxBroadcaster& aBC     ,
80                     const   SfxHint&        aHint   ) override ;
81 };
82 
impl_Size_Object2Struct(const Size & aSize)83 static awt::Size impl_Size_Object2Struct( const Size& aSize )
84 {
85     awt::Size aReturnValue;
86     aReturnValue.Width  = aSize.Width()  ;
87     aReturnValue.Height = aSize.Height() ;
88     return aReturnValue ;
89 }
90 
impl_Size_Struct2Object(const awt::Size & aSize)91 static Size impl_Size_Struct2Object( const awt::Size& aSize )
92 {
93     Size aReturnValue;
94     aReturnValue.setWidth( aSize.Width )  ;
95     aReturnValue.setHeight( aSize.Height ) ;
96     return aReturnValue ;
97 }
98 
99 class SfxPrintJob_Impl : public cppu::WeakImplHelper
100 <
101     css::view::XPrintJob
102 >
103 {
104     IMPL_PrintListener_DataContainer* m_pData;
105 
106 public:
107     explicit SfxPrintJob_Impl( IMPL_PrintListener_DataContainer* pData );
108     virtual Sequence< css::beans::PropertyValue > SAL_CALL getPrintOptions(  ) override;
109     virtual Sequence< css::beans::PropertyValue > SAL_CALL getPrinter(  ) override;
110     virtual Reference< css::view::XPrintable > SAL_CALL getPrintable(  ) override;
111     virtual void SAL_CALL cancelJob() override;
112 };
113 
SfxPrintJob_Impl(IMPL_PrintListener_DataContainer * pData)114 SfxPrintJob_Impl::SfxPrintJob_Impl( IMPL_PrintListener_DataContainer* pData )
115     : m_pData( pData )
116 {
117 }
118 
getPrintOptions()119 Sequence< css::beans::PropertyValue > SAL_CALL SfxPrintJob_Impl::getPrintOptions()
120 {
121     return m_pData->m_aPrintOptions;
122 }
123 
getPrinter()124 Sequence< css::beans::PropertyValue > SAL_CALL SfxPrintJob_Impl::getPrinter()
125 {
126     if( m_pData->m_pObjectShell.is() )
127     {
128         Reference < view::XPrintable > xPrintable( m_pData->m_pObjectShell->GetModel(), UNO_QUERY );
129         if ( xPrintable.is() )
130             return xPrintable->getPrinter();
131     }
132     return Sequence< css::beans::PropertyValue >();
133 }
134 
getPrintable()135 Reference< css::view::XPrintable > SAL_CALL SfxPrintJob_Impl::getPrintable()
136 {
137     Reference < view::XPrintable > xPrintable( m_pData->m_pObjectShell.is() ? m_pData->m_pObjectShell->GetModel() : nullptr, UNO_QUERY );
138     return xPrintable;
139 }
140 
cancelJob()141 void SAL_CALL SfxPrintJob_Impl::cancelJob()
142 {
143     // FIXME: how to cancel PrintJob via API?!
144     if( m_pData->m_pObjectShell.is() )
145         m_pData->m_pObjectShell->Broadcast( SfxPrintingHint( SFX_PRINTABLESTATE_CANCELJOB ) );
146 }
147 
SfxPrintHelper()148 SfxPrintHelper::SfxPrintHelper()
149 {
150     m_pData.reset(new IMPL_PrintListener_DataContainer(m_aMutex));
151 }
152 
initialize(const css::uno::Sequence<css::uno::Any> & aArguments)153 void SAL_CALL SfxPrintHelper::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
154 {
155     if ( !aArguments.hasElements() )
156         return;
157 
158     css::uno::Reference < css::frame::XModel > xModel;
159     aArguments[0] >>= xModel;
160     uno::Reference < lang::XUnoTunnel > xObj( xModel, uno::UNO_QUERY );
161     uno::Sequence < sal_Int8 > aSeq( SvGlobalName( SFX_GLOBAL_CLASSID ).GetByteSequence() );
162     sal_Int64 nHandle = xObj->getSomething( aSeq );
163     if ( nHandle )
164     {
165         m_pData->m_pObjectShell = reinterpret_cast< SfxObjectShell* >( sal::static_int_cast< sal_IntPtr >( nHandle ));
166         m_pData->StartListening(*m_pData->m_pObjectShell);
167     }
168 }
169 
~SfxPrintHelper()170 SfxPrintHelper::~SfxPrintHelper()
171 {
172 }
173 
174 namespace
175 {
convertToPaperFormat(Paper eFormat)176     view::PaperFormat convertToPaperFormat(Paper eFormat)
177     {
178         view::PaperFormat eRet;
179         switch (eFormat)
180         {
181             case PAPER_A3:
182                 eRet = view::PaperFormat_A3;
183                 break;
184             case PAPER_A4:
185                 eRet = view::PaperFormat_A4;
186                 break;
187             case PAPER_A5:
188                 eRet = view::PaperFormat_A5;
189                 break;
190             case PAPER_B4_ISO:
191                 eRet = view::PaperFormat_B4;
192                 break;
193             case PAPER_B5_ISO:
194                 eRet = view::PaperFormat_B5;
195                 break;
196             case PAPER_LETTER:
197                 eRet = view::PaperFormat_LETTER;
198                 break;
199             case PAPER_LEGAL:
200                 eRet = view::PaperFormat_LEGAL;
201                 break;
202             case PAPER_TABLOID:
203                 eRet = view::PaperFormat_TABLOID;
204                 break;
205             case PAPER_USER:
206             default:
207                 eRet = view::PaperFormat_USER;
208                 break;
209         }
210         return eRet;
211     }
212 
convertToPaper(view::PaperFormat eFormat)213     Paper convertToPaper(view::PaperFormat eFormat)
214     {
215         Paper eRet(PAPER_USER);
216         switch (eFormat)
217         {
218             case view::PaperFormat_A3:
219                 eRet = PAPER_A3;
220                 break;
221             case view::PaperFormat_A4:
222                 eRet = PAPER_A4;
223                 break;
224             case view::PaperFormat_A5:
225                 eRet = PAPER_A5;
226                 break;
227             case view::PaperFormat_B4:
228                 eRet = PAPER_B4_ISO;
229                 break;
230             case view::PaperFormat_B5:
231                 eRet = PAPER_B5_ISO;
232                 break;
233             case view::PaperFormat_LETTER:
234                 eRet = PAPER_LETTER;
235                 break;
236             case view::PaperFormat_LEGAL:
237                 eRet = PAPER_LEGAL;
238                 break;
239             case view::PaperFormat_TABLOID:
240                 eRet = PAPER_TABLOID;
241                 break;
242             case view::PaperFormat_USER:
243                 eRet = PAPER_USER;
244                 break;
245             case view::PaperFormat::PaperFormat_MAKE_FIXED_SIZE:
246                 break;
247             //deliberate no default to force warn on a new papersize
248         }
249         return eRet;
250     }
251 }
252 
253 
254 //  XPrintable
255 
256 
getPrinter()257 uno::Sequence< beans::PropertyValue > SAL_CALL SfxPrintHelper::getPrinter()
258 {
259     // object already disposed?
260     SolarMutexGuard aGuard;
261 
262     // search for any view of this document that is currently printing
263     const Printer *pPrinter = nullptr;
264     SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ? SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
265     SfxViewFrame* pFirst = pViewFrm;
266     while ( pViewFrm && !pPrinter )
267     {
268         pPrinter = pViewFrm->GetViewShell()->GetActivePrinter();
269         pViewFrm = SfxViewFrame::GetNext( *pViewFrm, m_pData->m_pObjectShell.get(), false );
270     }
271 
272     // if no view is printing currently, use the permanent SfxPrinter instance
273     if ( !pPrinter && pFirst )
274         pPrinter = pFirst->GetViewShell()->GetPrinter(true);
275 
276     if ( !pPrinter )
277         return uno::Sequence< beans::PropertyValue >();
278 
279     uno::Sequence< beans::PropertyValue > aPrinter(8);
280 
281     aPrinter.getArray()[7].Name = "CanSetPaperSize";
282     aPrinter.getArray()[7].Value <<= pPrinter->HasSupport( PrinterSupport::SetPaperSize );
283 
284     aPrinter.getArray()[6].Name = "CanSetPaperFormat";
285     aPrinter.getArray()[6].Value <<= pPrinter->HasSupport( PrinterSupport::SetPaper );
286 
287     aPrinter.getArray()[5].Name = "CanSetPaperOrientation";
288     aPrinter.getArray()[5].Value <<= pPrinter->HasSupport( PrinterSupport::SetOrientation );
289 
290     aPrinter.getArray()[4].Name = "IsBusy";
291     aPrinter.getArray()[4].Value <<= pPrinter->IsPrinting();
292 
293     aPrinter.getArray()[3].Name = "PaperSize";
294     awt::Size aSize = impl_Size_Object2Struct(pPrinter->GetPaperSize() );
295     aPrinter.getArray()[3].Value <<= aSize;
296 
297     aPrinter.getArray()[2].Name = "PaperFormat";
298     view::PaperFormat eFormat = convertToPaperFormat(pPrinter->GetPaper());
299     aPrinter.getArray()[2].Value <<= eFormat;
300 
301     aPrinter.getArray()[1].Name = "PaperOrientation";
302     view::PaperOrientation eOrient = static_cast<view::PaperOrientation>(pPrinter->GetOrientation());
303     aPrinter.getArray()[1].Value <<= eOrient;
304 
305     aPrinter.getArray()[0].Name = "Name";
306     OUString sStringTemp = pPrinter->GetName() ;
307     aPrinter.getArray()[0].Value <<= sStringTemp;
308 
309     return aPrinter;
310 }
311 
312 
313 //  XPrintable
314 
315 
impl_setPrinter(const uno::Sequence<beans::PropertyValue> & rPrinter,VclPtr<SfxPrinter> & pPrinter,SfxPrinterChangeFlags & nChangeFlags,SfxViewShell * & pViewSh)316 void SfxPrintHelper::impl_setPrinter(const uno::Sequence< beans::PropertyValue >& rPrinter,
317                                      VclPtr<SfxPrinter>& pPrinter,
318                                      SfxPrinterChangeFlags& nChangeFlags,
319                                      SfxViewShell*& pViewSh)
320 
321 {
322     // Get old Printer
323     SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ?
324                                 SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
325     if ( !pViewFrm )
326         return;
327 
328     pViewSh = pViewFrm->GetViewShell();
329     pPrinter = pViewSh->GetPrinter(true);
330     if ( !pPrinter )
331         return;
332 
333     // new Printer-Name available?
334     nChangeFlags = SfxPrinterChangeFlags::NONE;
335     sal_Int32 lDummy = 0;
336     auto pProp = std::find_if(rPrinter.begin(), rPrinter.end(),
337         [](const beans::PropertyValue &rProp) { return rProp.Name == "Name"; });
338     if (pProp != rPrinter.end())
339     {
340         OUString aPrinterName;
341         if ( ! ( pProp->Value >>= aPrinterName ) )
342             throw css::lang::IllegalArgumentException();
343 
344         if ( aPrinterName != pPrinter->GetName() )
345         {
346             pPrinter = VclPtr<SfxPrinter>::Create( pPrinter->GetOptions().Clone(), aPrinterName );
347             nChangeFlags = SfxPrinterChangeFlags::PRINTER;
348         }
349     }
350 
351     Size aSetPaperSize( 0, 0);
352     view::PaperFormat nPaperFormat = view::PaperFormat_USER;
353 
354     // other properties
355     for ( const beans::PropertyValue &rProp : rPrinter )
356     {
357         // get Property-Value from printer description
358         // PaperOrientation-Property?
359         if ( rProp.Name == "PaperOrientation" )
360         {
361             view::PaperOrientation eOrient;
362             if ( !( rProp.Value >>= eOrient ) )
363             {
364                 if ( !( rProp.Value >>= lDummy ) )
365                     throw css::lang::IllegalArgumentException();
366                 eOrient = static_cast<view::PaperOrientation>(lDummy);
367             }
368 
369             if ( static_cast<Orientation>(eOrient) != pPrinter->GetOrientation() )
370             {
371                 pPrinter->SetOrientation( static_cast<Orientation>(eOrient) );
372                 nChangeFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
373             }
374         }
375 
376         // PaperFormat-Property?
377         else if ( rProp.Name == "PaperFormat" )
378         {
379             if ( !( rProp.Value >>= nPaperFormat ) )
380             {
381                 if ( !( rProp.Value >>= lDummy ) )
382                     throw css::lang::IllegalArgumentException();
383                 nPaperFormat = static_cast<view::PaperFormat>(lDummy);
384             }
385 
386             if ( convertToPaper(nPaperFormat) != pPrinter->GetPaper() )
387             {
388                 pPrinter->SetPaper( convertToPaper(nPaperFormat) );
389                 nChangeFlags |= SfxPrinterChangeFlags::CHG_SIZE;
390             }
391         }
392 
393         // PaperSize-Property?
394         else if ( rProp.Name == "PaperSize" )
395         {
396             awt::Size aTempSize ;
397             if ( !( rProp.Value >>= aTempSize ) )
398             {
399                 throw css::lang::IllegalArgumentException();
400             }
401             aSetPaperSize = impl_Size_Struct2Object(aTempSize);
402         }
403 
404         // PrinterTray-Property
405         else if ( rProp.Name == "PrinterPaperTray" )
406         {
407             OUString aTmp;
408             if ( !( rProp.Value >>= aTmp ) )
409                 throw css::lang::IllegalArgumentException();
410             const sal_uInt16 nCount = pPrinter->GetPaperBinCount();
411             for (sal_uInt16 nBin=0; nBin<nCount; nBin++)
412             {
413                 OUString aName( pPrinter->GetPaperBinName(nBin) );
414                 if ( aName == aTmp )
415                 {
416                     pPrinter->SetPaperBin(nBin);
417                     break;
418                 }
419             }
420         }
421     }
422 
423     // The PaperSize may be set only when actually PAPER_USER
424     // applies, otherwise the driver could choose an invalid format.
425     if(nPaperFormat == view::PaperFormat_USER && aSetPaperSize.Width())
426     {
427         // Bug 56929 - MapMode of 100mm which recalculated when
428         // the device is set. Additionally only set if they were really changed.
429         aSetPaperSize = pPrinter->LogicToPixel(aSetPaperSize, MapMode(MapUnit::Map100thMM));
430         if( aSetPaperSize != pPrinter->GetPaperSizePixel() )
431         {
432             pPrinter->SetPaperSizeUser( pPrinter->PixelToLogic( aSetPaperSize ) );
433             nChangeFlags |= SfxPrinterChangeFlags::CHG_SIZE;
434         }
435     }
436 
437     //wait until printing is done
438     SfxPrinter* pDocPrinter = pViewSh->GetPrinter();
439     while ( pDocPrinter->IsPrinting() )
440         Application::Yield();
441 }
442 
setPrinter(const uno::Sequence<beans::PropertyValue> & rPrinter)443 void SAL_CALL SfxPrintHelper::setPrinter(const uno::Sequence< beans::PropertyValue >& rPrinter)
444 {
445     // object already disposed?
446     SolarMutexGuard aGuard;
447 
448     SfxViewShell* pViewSh = nullptr;
449     VclPtr<SfxPrinter> pPrinter;
450     SfxPrinterChangeFlags nChangeFlags = SfxPrinterChangeFlags::NONE;
451     impl_setPrinter(rPrinter,pPrinter,nChangeFlags,pViewSh);
452     // set new printer
453     if ( pViewSh && pPrinter )
454         pViewSh->SetPrinter( pPrinter, nChangeFlags );
455 }
456 
457 
458 //  ImplPrintWatch thread for asynchronous printing with moving temp. file to ucb location
459 
460 
461 /* This implements a thread which will be started to wait for asynchronous
462    print jobs to temp. locally files. If they finish we move the temp. files
463    to their right locations by using the ucb.
464  */
465 class ImplUCBPrintWatcher : public ::osl::Thread
466 {
467     private:
468         /// of course we must know the printer which execute the job
469         VclPtr<SfxPrinter> m_pPrinter;
470         /// this describes the target location for the printed temp file
471         OUString const m_sTargetURL;
472         /// it holds the temp file alive, till the print job will finish and remove it from disk automatically if the object die
473         ::utl::TempFile* m_pTempFile;
474 
475     public:
476         /* initialize this watcher but don't start it */
ImplUCBPrintWatcher(SfxPrinter * pPrinter,::utl::TempFile * pTempFile,const OUString & sTargetURL)477         ImplUCBPrintWatcher( SfxPrinter* pPrinter, ::utl::TempFile* pTempFile, const OUString& sTargetURL )
478                 : m_pPrinter  ( pPrinter   )
479                 , m_sTargetURL( sTargetURL )
480                 , m_pTempFile ( pTempFile  )
481         {}
482 
483         /* waits for finishing of the print job and moves the temp file afterwards
484            Note: Starting of the job is done outside this thread!
485            But we have to free some of the given resources on heap!
486          */
run()487         void SAL_CALL run() override
488         {
489             osl_setThreadName("ImplUCBPrintWatcher");
490 
491             /* SAFE { */
492             {
493                 SolarMutexGuard aGuard;
494                 while( m_pPrinter->IsPrinting() )
495                     Application::Yield();
496                 m_pPrinter.clear(); // don't delete it! It's borrowed only :-)
497             }
498             /* } SAFE */
499 
500             // lock for further using of our member isn't necessary - because
501             // we run alone by definition. Nobody join for us nor use us...
502             moveAndDeleteTemp(&m_pTempFile,m_sTargetURL);
503 
504             // finishing of this run() method will call onTerminate() automatically
505             // kill this thread there!
506         }
507 
508         /* nobody wait for this thread. We must kill ourself ...
509          */
onTerminated()510         void SAL_CALL onTerminated() override
511         {
512             delete this;
513         }
514 
515         /* static helper to move the temp. file to the target location by using the ucb
516            It's static to be usable from outside too. So it's not really necessary to start
517            the thread, if finishing of the job was detected outside this thread.
518            But it must be called without using a corresponding thread for the given parameter!
519          */
moveAndDeleteTemp(::utl::TempFile ** ppTempFile,const OUString & sTargetURL)520         static void moveAndDeleteTemp( ::utl::TempFile** ppTempFile, const OUString& sTargetURL )
521         {
522             // move the file
523             try
524             {
525                 INetURLObject aSplitter(sTargetURL);
526                 OUString        sFileName = aSplitter.getName(
527                                             INetURLObject::LAST_SEGMENT,
528                                             true,
529                                             INetURLObject::DecodeMechanism::WithCharset);
530                 if (aSplitter.removeSegment() && !sFileName.isEmpty())
531                 {
532                     ::ucbhelper::Content aSource(
533                             (*ppTempFile)->GetURL(),
534                             css::uno::Reference< css::ucb::XCommandEnvironment >(),
535                             comphelper::getProcessComponentContext());
536 
537                     ::ucbhelper::Content aTarget(
538                             aSplitter.GetMainURL(INetURLObject::DecodeMechanism::NONE),
539                             css::uno::Reference< css::ucb::XCommandEnvironment >(),
540                             comphelper::getProcessComponentContext());
541 
542                     aTarget.transferContent(
543                             aSource,
544                             ::ucbhelper::InsertOperation::Copy,
545                             sFileName,
546                             css::ucb::NameClash::OVERWRITE);
547                 }
548             }
549             catch (const css::ucb::ContentCreationException&)
550             {
551                 OSL_FAIL("content create exception");
552             }
553             catch (const css::ucb::CommandAbortedException&)
554             {
555                 OSL_FAIL("command abort exception");
556             }
557             catch (const css::uno::RuntimeException&)
558             {
559                 OSL_FAIL("runtime exception");
560             }
561             catch (const css::uno::Exception&)
562             {
563                 OSL_FAIL("unknown exception");
564             }
565 
566             // kill the temp file!
567             delete *ppTempFile;
568             *ppTempFile = nullptr;
569         }
570 };
571 
572 
573 //  XPrintable
574 
print(const uno::Sequence<beans::PropertyValue> & rOptions)575 void SAL_CALL SfxPrintHelper::print(const uno::Sequence< beans::PropertyValue >& rOptions)
576 {
577     if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
578         return;
579 
580     // object already disposed?
581     // object already disposed?
582     SolarMutexGuard aGuard;
583 
584     // get view for sfx printing capabilities
585     SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ?
586                                 SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
587     if ( !pViewFrm )
588         return;
589     SfxViewShell* pView = pViewFrm->GetViewShell();
590     if ( !pView )
591         return;
592     bool bMonitor = false;
593     // We need this information at the end of this method, if we start the vcl printer
594     // by executing the slot. Because if it is a ucb relevant URL we must wait for
595     // finishing the print job and move the temporary local file by using the ucb
596     // to the right location. But in case of no file name is given or it is already
597     // a local one we can suppress this special handling. Because then vcl makes all
598     // right for us.
599     OUString sUcbUrl;
600     ::utl::TempFile* pUCBPrintTempFile = nullptr;
601 
602     uno::Sequence < beans::PropertyValue > aCheckedArgs( rOptions.getLength() );
603     sal_Int32 nProps = 0;
604     bool  bWaitUntilEnd = false;
605     sal_Int16 nDuplexMode = css::view::DuplexMode::UNKNOWN;
606     for ( const beans::PropertyValue &rProp : rOptions )
607     {
608         // get Property-Value from options
609         // FileName-Property?
610         if ( rProp.Name == "FileName" )
611         {
612             // unpack th URL and check for a valid and well known protocol
613             OUString sTemp;
614             if (
615                 ( rProp.Value.getValueType()!=cppu::UnoType<OUString>::get())  ||
616                 (!(rProp.Value>>=sTemp))
617                )
618             {
619                 throw css::lang::IllegalArgumentException();
620             }
621 
622             OUString      sPath;
623             OUString      sURL  (sTemp);
624             INetURLObject aCheck(sURL );
625             if (aCheck.GetProtocol()==INetProtocol::NotValid)
626             {
627                 // OK - it's not a valid URL. But may it's a simple
628                 // system path directly. It will be supported for historical
629                 // reasons. Otherwise we break too much external code...
630                 // We try to convert it to a file URL. If its possible
631                 // we put the system path to the item set and let vcl work with it.
632                 // No ucb or thread will be necessary then. In case it couldn't be
633                 // converted it's not a URL nor a system path. Then we can't accept
634                 // this parameter and have to throw an exception.
635                 const OUString& sSystemPath(sTemp);
636                 OUString sFileURL;
637                 if (::osl::FileBase::getFileURLFromSystemPath(sSystemPath,sFileURL)!=::osl::FileBase::E_None)
638                     throw css::lang::IllegalArgumentException();
639                 aCheckedArgs[nProps].Name = rProp.Name;
640                 aCheckedArgs[nProps++].Value <<= sFileURL;
641                 // and append the local filename
642                 aCheckedArgs.realloc( aCheckedArgs.getLength()+1 );
643                 aCheckedArgs[nProps].Name = "LocalFileName";
644                 aCheckedArgs[nProps++].Value <<= sTemp;
645             }
646             else
647             // It's a valid URL. but now we must know, if it is a local one or not.
648             // It's a question of using ucb or not!
649             if (osl::FileBase::getSystemPathFromFileURL(sURL, sPath) == osl::FileBase::E_None)
650             {
651                 // it's a local file, we can use vcl without special handling
652                 // And we have to use the system notation of the incoming URL.
653                 // But it into the descriptor and let the slot be executed at
654                 // the end of this method.
655                 aCheckedArgs[nProps].Name = rProp.Name;
656                 aCheckedArgs[nProps++].Value <<= sTemp;
657                 // and append the local filename
658                 aCheckedArgs.realloc( aCheckedArgs.getLength()+1 );
659                 aCheckedArgs[nProps].Name = "LocalFileName";
660                 aCheckedArgs[nProps++].Value <<= sPath;
661             }
662             else
663             {
664                 // it's a ucb target. So we must use a temp. file for vcl
665                 // and move it after printing by using the ucb.
666                 // Create a temp file on the heap (because it must delete the
667                 // real file on disk automatically if it die - bt we have to share it with
668                 // some other sources ... e.g. the ImplUCBPrintWatcher).
669                 // And we put the name of this temp file to the descriptor instead
670                 // of the URL. The URL we save for later using separately.
671                 // Execution of the print job will be done later by executing
672                 // a slot ...
673                 if(!pUCBPrintTempFile)
674                     pUCBPrintTempFile = new ::utl::TempFile();
675                 pUCBPrintTempFile->EnableKillingFile();
676 
677                 //FIXME: does it work?
678                 aCheckedArgs[nProps].Name = "LocalFileName";
679                 aCheckedArgs[nProps++].Value <<= pUCBPrintTempFile->GetFileName();
680                 sUcbUrl = sURL;
681             }
682         }
683 
684         // CopyCount-Property
685         else if ( rProp.Name == "CopyCount" )
686         {
687             sal_Int32 nCopies = 0;
688             if ( !( rProp.Value >>= nCopies ) )
689                 throw css::lang::IllegalArgumentException();
690             aCheckedArgs[nProps].Name = rProp.Name;
691             aCheckedArgs[nProps++].Value <<= nCopies;
692         }
693 
694         // Collate-Property
695         // Sort-Property (deprecated)
696         else if ( rProp.Name == "Collate" || rProp.Name == "Sort" )
697         {
698             bool bTemp;
699             if ( !(rProp.Value >>= bTemp) )
700                 throw css::lang::IllegalArgumentException();
701             aCheckedArgs[nProps].Name = "Collate";
702             aCheckedArgs[nProps++].Value <<= bTemp;
703         }
704 
705         // Pages-Property
706         else if ( rProp.Name == "Pages" )
707         {
708             OUString sTemp;
709             if( !(rProp.Value >>= sTemp) )
710                 throw css::lang::IllegalArgumentException();
711             aCheckedArgs[nProps].Name = rProp.Name;
712             aCheckedArgs[nProps++].Value <<= sTemp;
713         }
714 
715         // MonitorVisible
716         else if ( rProp.Name == "MonitorVisible" )
717         {
718             if( !(rProp.Value >>= bMonitor) )
719                 throw css::lang::IllegalArgumentException();
720             aCheckedArgs[nProps].Name = rProp.Name;
721             aCheckedArgs[nProps++].Value <<= bMonitor;
722         }
723 
724         // Wait
725         else if ( rProp.Name == "Wait" )
726         {
727             if ( !(rProp.Value >>= bWaitUntilEnd) )
728                 throw css::lang::IllegalArgumentException();
729             aCheckedArgs[nProps].Name = rProp.Name;
730             aCheckedArgs[nProps++].Value <<= bWaitUntilEnd;
731         }
732 
733         else if ( rProp.Name == "DuplexMode" )
734         {
735             if ( !(rProp.Value >>= nDuplexMode ) )
736                 throw css::lang::IllegalArgumentException();
737             aCheckedArgs[nProps].Name = rProp.Name;
738             aCheckedArgs[nProps++].Value <<= nDuplexMode;
739         }
740     }
741 
742     if ( nProps != aCheckedArgs.getLength() )
743         aCheckedArgs.realloc(nProps);
744 
745     // Execute the print request every time.
746     // It doesn't matter if it is a real printer used or we print to a local file
747     // nor if we print to a temp file and move it afterwards by using the ucb.
748     // That will be handled later. see pUCBPrintFile below!
749     pView->ExecPrint( aCheckedArgs, true, false );
750 
751     // Ok - may be execution before has finished (or started!) printing.
752     // And may it was a printing to a file.
753     // Now we have to check if we can move the file (if necessary) via UCB to its right location.
754     // Cases:
755     //  a) printing finished                        => move the file directly and forget the watcher thread
756     //  b) printing is asynchron and runs currently => start watcher thread and exit this method
757     //                                                 This thread make all necessary things by itself.
758     if (!pUCBPrintTempFile)
759         return;
760 
761     // a)
762     SfxPrinter* pPrinter = pView->GetPrinter();
763     if ( ! pPrinter->IsPrinting() )
764         ImplUCBPrintWatcher::moveAndDeleteTemp(&pUCBPrintTempFile,sUcbUrl);
765     // b)
766     else
767     {
768         // Note: we create(d) some resource on the heap (thread and temp file).
769         // They will be deleted by the thread automatically if it finishes its run() method.
770         ImplUCBPrintWatcher* pWatcher = new ImplUCBPrintWatcher( pPrinter, pUCBPrintTempFile, sUcbUrl );
771         pWatcher->create();
772     }
773 }
774 
Notify(SfxBroadcaster & rBC,const SfxHint & rHint)775 void IMPL_PrintListener_DataContainer::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
776 {
777     const SfxPrintingHint* pPrintHint = dynamic_cast<const SfxPrintingHint*>(&rHint);
778     if ( &rBC != m_pObjectShell.get()
779         || !pPrintHint
780         || pPrintHint->GetWhich() == SFX_PRINTABLESTATE_CANCELJOB )
781         return;
782 
783     if ( pPrintHint->GetWhich() == css::view::PrintableState_JOB_STARTED )
784     {
785         if ( !m_xPrintJob.is() )
786             m_xPrintJob = new SfxPrintJob_Impl( this );
787         m_aPrintOptions = pPrintHint->GetOptions();
788     }
789 
790     ::cppu::OInterfaceContainerHelper* pContainer = m_aInterfaceContainer.getContainer(
791         cppu::UnoType<view::XPrintJobListener>::get());
792     if ( !pContainer )
793         return;
794 
795     view::PrintJobEvent aEvent;
796     aEvent.Source = m_xPrintJob;
797     aEvent.State = pPrintHint->GetWhich();
798 
799     ::cppu::OInterfaceIteratorHelper pIterator(*pContainer);
800     while (pIterator.hasMoreElements())
801         static_cast<view::XPrintJobListener*>(pIterator.next())->printJobEvent( aEvent );
802 }
803 
addPrintJobListener(const css::uno::Reference<css::view::XPrintJobListener> & xListener)804 void SAL_CALL SfxPrintHelper::addPrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener )
805 {
806     SolarMutexGuard aGuard;
807     m_pData->m_aInterfaceContainer.addInterface( cppu::UnoType<view::XPrintJobListener>::get(), xListener );
808 }
809 
removePrintJobListener(const css::uno::Reference<css::view::XPrintJobListener> & xListener)810 void SAL_CALL SfxPrintHelper::removePrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener )
811 {
812     SolarMutexGuard aGuard;
813     m_pData->m_aInterfaceContainer.removeInterface( cppu::UnoType<view::XPrintJobListener>::get(), xListener );
814 }
815 
816 
817 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
818