summaryrefslogtreecommitdiffstats
path: root/sfx2/source/doc/printhelper.cxx
blob: 6ca15e6cdbd9d7d50be9c3349b624d9129f1110e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include "printhelper.hxx"

#include <com/sun/star/view/XPrintJob.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/view/PaperFormat.hpp>
#include <com/sun/star/view/PaperOrientation.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/view/DuplexMode.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertyvalue.hxx>
#include <svl/itemset.hxx>
#include <svl/lstner.hxx>
#include <unotools/tempfile.hxx>
#include <osl/file.hxx>
#include <osl/thread.hxx>
#include <tools/urlobj.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <ucbhelper/content.hxx>
#include <comphelper/interfacecontainer4.hxx>
#include <cppuhelper/implbase.hxx>
#include <utility>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>

#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/event.hxx>

#define SFX_PRINTABLESTATE_CANCELJOB    css::view::PrintableState(-2)

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

struct IMPL_PrintListener_DataContainer : public SfxListener
{
    SfxObjectShellRef                               m_pObjectShell;
    std::mutex                                      m_aMutex;
    comphelper::OInterfaceContainerHelper4<view::XPrintJobListener> m_aJobListeners;
    uno::Reference< css::view::XPrintJob>           m_xPrintJob;
    css::uno::Sequence< css::beans::PropertyValue > m_aPrintOptions;

    explicit IMPL_PrintListener_DataContainer()
    {
    }


    void Notify(            SfxBroadcaster& aBC     ,
                    const   SfxHint&        aHint   ) override ;
};

static awt::Size impl_Size_Object2Struct( const Size& aSize )
{
    awt::Size aReturnValue;
    aReturnValue.Width  = aSize.Width()  ;
    aReturnValue.Height = aSize.Height() ;
    return aReturnValue ;
}

static Size impl_Size_Struct2Object( const awt::Size& aSize )
{
    Size aReturnValue;
    aReturnValue.setWidth( aSize.Width )  ;
    aReturnValue.setHeight( aSize.Height ) ;
    return aReturnValue ;
}

namespace {

class SfxPrintJob_Impl : public cppu::WeakImplHelper
<
    css::view::XPrintJob
>
{
    IMPL_PrintListener_DataContainer* m_pData;

public:
    explicit SfxPrintJob_Impl( IMPL_PrintListener_DataContainer* pData );
    virtual Sequence< css::beans::PropertyValue > SAL_CALL getPrintOptions(  ) override;
    virtual Sequence< css::beans::PropertyValue > SAL_CALL getPrinter(  ) override;
    virtual Reference< css::view::XPrintable > SAL_CALL getPrintable(  ) override;
    virtual void SAL_CALL cancelJob() override;
};

}

SfxPrintJob_Impl::SfxPrintJob_Impl( IMPL_PrintListener_DataContainer* pData )
    : m_pData( pData )
{
}

Sequence< css::beans::PropertyValue > SAL_CALL SfxPrintJob_Impl::getPrintOptions()
{
    return m_pData->m_aPrintOptions;
}

Sequence< css::beans::PropertyValue > SAL_CALL SfxPrintJob_Impl::getPrinter()
{
    if( m_pData->m_pObjectShell.is() )
    {
        Reference < view::XPrintable > xPrintable( m_pData->m_pObjectShell->GetModel(), UNO_QUERY );
        if ( xPrintable.is() )
            return xPrintable->getPrinter();
    }
    return Sequence< css::beans::PropertyValue >();
}

Reference< css::view::XPrintable > SAL_CALL SfxPrintJob_Impl::getPrintable()
{
    Reference < view::XPrintable > xPrintable( m_pData->m_pObjectShell.is() ? m_pData->m_pObjectShell->GetModel() : nullptr, UNO_QUERY );
    return xPrintable;
}

void SAL_CALL SfxPrintJob_Impl::cancelJob()
{
    // FIXME: how to cancel PrintJob via API?!
    if( m_pData->m_pObjectShell.is() )
        m_pData->m_pObjectShell->Broadcast( SfxPrintingHint( SFX_PRINTABLESTATE_CANCELJOB ) );
}

SfxPrintHelper::SfxPrintHelper()
{
    m_pData.reset(new IMPL_PrintListener_DataContainer());
}

void SAL_CALL SfxPrintHelper::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
{
    if ( !aArguments.hasElements() )
        return;

    css::uno::Reference < css::frame::XModel > xModel;
    aArguments[0] >>= xModel;
    m_pData->m_pObjectShell = SfxObjectShell::GetShellFromComponent(xModel);
    if (m_pData->m_pObjectShell)
        m_pData->StartListening(*m_pData->m_pObjectShell);
}

SfxPrintHelper::~SfxPrintHelper()
{
}

namespace
{
    view::PaperFormat convertToPaperFormat(Paper eFormat)
    {
        view::PaperFormat eRet;
        switch (eFormat)
        {
            case PAPER_A3:
                eRet = view::PaperFormat_A3;
                break;
            case PAPER_A4:
                eRet = view::PaperFormat_A4;
                break;
            case PAPER_A5:
                eRet = view::PaperFormat_A5;
                break;
            case PAPER_B4_ISO:
                eRet = view::PaperFormat_B4;
                break;
            case PAPER_B5_ISO:
                eRet = view::PaperFormat_B5;
                break;
            case PAPER_LETTER:
                eRet = view::PaperFormat_LETTER;
                break;
            case PAPER_LEGAL:
                eRet = view::PaperFormat_LEGAL;
                break;
            case PAPER_TABLOID:
                eRet = view::PaperFormat_TABLOID;
                break;
            case PAPER_USER:
            default:
                eRet = view::PaperFormat_USER;
                break;
        }
        return eRet;
    }

    Paper convertToPaper(view::PaperFormat eFormat)
    {
        Paper eRet(PAPER_USER);
        switch (eFormat)
        {
            case view::PaperFormat_A3:
                eRet = PAPER_A3;
                break;
            case view::PaperFormat_A4:
                eRet = PAPER_A4;
                break;
            case view::PaperFormat_A5:
                eRet = PAPER_A5;
                break;
            case view::PaperFormat_B4:
                eRet = PAPER_B4_ISO;
                break;
            case view::PaperFormat_B5:
                eRet = PAPER_B5_ISO;
                break;
            case view::PaperFormat_LETTER:
                eRet = PAPER_LETTER;
                break;
            case view::PaperFormat_LEGAL:
                eRet = PAPER_LEGAL;
                break;
            case view::PaperFormat_TABLOID:
                eRet = PAPER_TABLOID;
                break;
            case view::PaperFormat_USER:
                eRet = PAPER_USER;
                break;
            case view::PaperFormat::PaperFormat_MAKE_FIXED_SIZE:
                break;
            //deliberate no default to force warn on a new papersize
        }
        return eRet;
    }
}


//  XPrintable


uno::Sequence< beans::PropertyValue > SAL_CALL SfxPrintHelper::getPrinter()
{
    // object already disposed?
    SolarMutexGuard aGuard;

    // search for any view of this document that is currently printing
    const Printer *pPrinter = nullptr;
    SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ? SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
    SfxViewFrame* pFirst = pViewFrm;
    while ( pViewFrm && !pPrinter )
    {
        pPrinter = pViewFrm->GetViewShell()->GetActivePrinter();
        pViewFrm = SfxViewFrame::GetNext( *pViewFrm, m_pData->m_pObjectShell.get(), false );
    }

    // if no view is printing currently, use the permanent SfxPrinter instance
    if ( !pPrinter && pFirst )
        pPrinter = pFirst->GetViewShell()->GetPrinter(true);

    if ( !pPrinter )
        return uno::Sequence< beans::PropertyValue >();

    return
    {
        comphelper::makePropertyValue("Name", pPrinter->GetName()),
        comphelper::makePropertyValue("PaperOrientation", static_cast<view::PaperOrientation>(pPrinter->GetOrientation())),
        comphelper::makePropertyValue("PaperFormat", convertToPaperFormat(pPrinter->GetPaper())),
        comphelper::makePropertyValue("PaperSize", impl_Size_Object2Struct(pPrinter->GetPaperSize() )),
        comphelper::makePropertyValue("IsBusy", pPrinter->IsPrinting()),
        comphelper::makePropertyValue("CanSetPaperOrientation", pPrinter->HasSupport( PrinterSupport::SetOrientation )),
        comphelper::makePropertyValue("CanSetPaperFormat", pPrinter->HasSupport( PrinterSupport::SetPaper )),
        comphelper::makePropertyValue("CanSetPaperSize", pPrinter->HasSupport( PrinterSupport::SetPaperSize ))
    };
}


//  XPrintable


void SfxPrintHelper::impl_setPrinter(const uno::Sequence< beans::PropertyValue >& rPrinter,
                                     VclPtr<SfxPrinter>& pPrinter,
                                     SfxPrinterChangeFlags& nChangeFlags,
                                     SfxViewShell*& pViewSh)

{
    // Get old Printer
    SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ?
                                SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
    if ( !pViewFrm )
        return;

    pViewSh = pViewFrm->GetViewShell();
    pPrinter = pViewSh->GetPrinter(true);
    if ( !pPrinter )
        return;

    // new Printer-Name available?
    nChangeFlags = SfxPrinterChangeFlags::NONE;
    sal_Int32 lDummy = 0;
    auto pProp = std::find_if(rPrinter.begin(), rPrinter.end(),
        [](const beans::PropertyValue &rProp) { return rProp.Name == "Name"; });
    if (pProp != rPrinter.end())
    {
        OUString aPrinterName;
        if ( ! ( pProp->Value >>= aPrinterName ) )
            throw css::lang::IllegalArgumentException();

        if ( aPrinterName != pPrinter->GetName() )
        {
            pPrinter = VclPtr<SfxPrinter>::Create( pPrinter->GetOptions().Clone(), aPrinterName );
            nChangeFlags = SfxPrinterChangeFlags::PRINTER;
        }
    }

    Size aSetPaperSize( 0, 0);
    view::PaperFormat nPaperFormat = view::PaperFormat_USER;

    // other properties
    for ( const beans::PropertyValue &rProp : rPrinter )
    {
        // get Property-Value from printer description
        // PaperOrientation-Property?
        if ( rProp.Name == "PaperOrientation" )
        {
            view::PaperOrientation eOrient;
            if ( !( rProp.Value >>= eOrient ) )
            {
                if ( !( rProp.Value >>= lDummy ) )
                    throw css::lang::IllegalArgumentException();
                eOrient = static_cast<view::PaperOrientation>(lDummy);
            }

            if ( static_cast<Orientation>(eOrient) != pPrinter->GetOrientation() )
            {
                pPrinter->SetOrientation( static_cast<Orientation>(eOrient) );
                nChangeFlags |= SfxPrinterChangeFlags::CHG_ORIENTATION;
            }
        }

        // PaperFormat-Property?
        else if ( rProp.Name == "PaperFormat" )
        {
            if ( !( rProp.Value >>= nPaperFormat ) )
            {
                if ( !( rProp.Value >>= lDummy ) )
                    throw css::lang::IllegalArgumentException();
                nPaperFormat = static_cast<view::PaperFormat>(lDummy);
            }

            if ( convertToPaper(nPaperFormat) != pPrinter->GetPaper() )
            {
                pPrinter->SetPaper( convertToPaper(nPaperFormat) );
                nChangeFlags |= SfxPrinterChangeFlags::CHG_SIZE;
            }
        }

        // PaperSize-Property?
        else if ( rProp.Name == "PaperSize" )
        {
            awt::Size aTempSize ;
            if ( !( rProp.Value >>= aTempSize ) )
            {
                throw css::lang::IllegalArgumentException();
            }
            aSetPaperSize = impl_Size_Struct2Object(aTempSize);
        }

        // PrinterTray-Property
        else if ( rProp.Name == "PrinterPaperTray" )
        {
            OUString aTmp;
            if ( !( rProp.Value >>= aTmp ) )
                throw css::lang::IllegalArgumentException();
            const sal_uInt16 nCount = pPrinter->GetPaperBinCount();
            for (sal_uInt16 nBin=0; nBin<nCount; nBin++)
            {
                OUString aName( pPrinter->GetPaperBinName(nBin) );
                if ( aName == aTmp )
                {
                    pPrinter->SetPaperBin(nBin);
                    break;
                }
            }
        }
    }

    // The PaperSize may be set only when actually PAPER_USER
    // applies, otherwise the driver could choose an invalid format.
    if(nPaperFormat == view::PaperFormat_USER && aSetPaperSize.Width())
    {
        // Bug 56929 - MapMode of 100mm which recalculated when
        // the device is set. Additionally only set if they were really changed.
        aSetPaperSize = pPrinter->LogicToPixel(aSetPaperSize, MapMode(MapUnit::Map100thMM));
        if( aSetPaperSize != pPrinter->GetPaperSizePixel() )
        {
            pPrinter->SetPaperSizeUser( pPrinter->PixelToLogic( aSetPaperSize ) );
            nChangeFlags |= SfxPrinterChangeFlags::CHG_SIZE;
        }
    }

    //wait until printing is done
    SfxPrinter* pDocPrinter = pViewSh->GetPrinter();
    while ( pDocPrinter->IsPrinting() && !Application::IsQuit())
        Application::Yield();
}

void SAL_CALL SfxPrintHelper::setPrinter(const uno::Sequence< beans::PropertyValue >& rPrinter)
{
    // object already disposed?
    SolarMutexGuard aGuard;

    SfxViewShell* pViewSh = nullptr;
    VclPtr<SfxPrinter> pPrinter;
    SfxPrinterChangeFlags nChangeFlags = SfxPrinterChangeFlags::NONE;
    impl_setPrinter(rPrinter,pPrinter,nChangeFlags,pViewSh);
    // set new printer
    if ( pViewSh && pPrinter )
        pViewSh->SetPrinter( pPrinter, nChangeFlags );
}


//  ImplPrintWatch thread for asynchronous printing with moving temp. file to ucb location

namespace {

/* This implements a thread which will be started to wait for asynchronous
   print jobs to temp. locally files. If they finish we move the temp. files
   to their right locations by using the ucb.
 */
class ImplUCBPrintWatcher : public ::osl::Thread
{
    private:
        /// of course we must know the printer which execute the job
        VclPtr<SfxPrinter> m_pPrinter;
        /// this describes the target location for the printed temp file
        OUString m_sTargetURL;
        /// it holds the temp file alive, till the print job will finish and remove it from disk automatically if the object die
        ::utl::TempFileNamed* m_pTempFile;

    public:
        /* initialize this watcher but don't start it */
        ImplUCBPrintWatcher( SfxPrinter* pPrinter, ::utl::TempFileNamed* pTempFile, OUString sTargetURL )
                : m_pPrinter  ( pPrinter   )
                , m_sTargetURL(std::move( sTargetURL ))
                , m_pTempFile ( pTempFile  )
        {}

        /* waits for finishing of the print job and moves the temp file afterwards
           Note: Starting of the job is done outside this thread!
           But we have to free some of the given resources on heap!
         */
        void SAL_CALL run() override
        {
            osl_setThreadName("ImplUCBPrintWatcher");

            /* SAFE { */
            {
                SolarMutexGuard aGuard;
                while( m_pPrinter->IsPrinting() && !Application::IsQuit())
                    Application::Yield();
                m_pPrinter.clear(); // don't delete it! It's borrowed only :-)
            }
            /* } SAFE */

            // lock for further using of our member isn't necessary - because
            // we run alone by definition. Nobody join for us nor use us...
            moveAndDeleteTemp(&m_pTempFile,m_sTargetURL);

            // finishing of this run() method will call onTerminate() automatically
            // kill this thread there!
        }

        /* nobody wait for this thread. We must kill ourself ...
         */
        void SAL_CALL onTerminated() override
        {
            delete this;
        }

        /* static helper to move the temp. file to the target location by using the ucb
           It's static to be usable from outside too. So it's not really necessary to start
           the thread, if finishing of the job was detected outside this thread.
           But it must be called without using a corresponding thread for the given parameter!
         */
        static void moveAndDeleteTemp( ::utl::TempFileNamed** ppTempFile, std::u16string_view sTargetURL )
        {
            // move the file
            try
            {
                INetURLObject aSplitter(sTargetURL);
                OUString        sFileName = aSplitter.getName(
                                            INetURLObject::LAST_SEGMENT,
                                            true,
                                            INetURLObject::DecodeMechanism::WithCharset);
                if (aSplitter.removeSegment() && !sFileName.isEmpty())
                {
                    ::ucbhelper::Content aSource(
                            (*ppTempFile)->GetURL(),
                            css::uno::Reference< css::ucb::XCommandEnvironment >(),
                            comphelper::getProcessComponentContext());

                    ::ucbhelper::Content aTarget(
                            aSplitter.GetMainURL(INetURLObject::DecodeMechanism::NONE),
                            css::uno::Reference< css::ucb::XCommandEnvironment >(),
                            comphelper::getProcessComponentContext());

                    aTarget.transferContent(
                            aSource,
                            ::ucbhelper::InsertOperation::Copy,
                            sFileName,
                            css::ucb::NameClash::OVERWRITE);
                }
            }
            catch (const css::uno::Exception&)
            {
                TOOLS_WARN_EXCEPTION( "sfx.doc", "");
            }

            // kill the temp file!
            delete *ppTempFile;
            *ppTempFile = nullptr;
        }
};

}

//  XPrintable

void SAL_CALL SfxPrintHelper::print(const uno::Sequence< beans::PropertyValue >& rOptions)
{
    if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
        return;

    // object already disposed?
    // object already disposed?
    SolarMutexGuard aGuard;

    // get view for sfx printing capabilities
    SfxViewFrame *pViewFrm = m_pData->m_pObjectShell.is() ?
                                SfxViewFrame::GetFirst( m_pData->m_pObjectShell.get(), false ) : nullptr;
    if ( !pViewFrm )
        return;
    SfxViewShell* pView = pViewFrm->GetViewShell();
    if ( !pView )
        return;
    bool bMonitor = false;
    // We need this information at the end of this method, if we start the vcl printer
    // by executing the slot. Because if it is a ucb relevant URL we must wait for
    // finishing the print job and move the temporary local file by using the ucb
    // to the right location. But in case of no file name is given or it is already
    // a local one we can suppress this special handling. Because then vcl makes all
    // right for us.
    OUString sUcbUrl;
    ::utl::TempFileNamed* pUCBPrintTempFile = nullptr;

    uno::Sequence < beans::PropertyValue > aCheckedArgs( rOptions.getLength() );
    auto pCheckedArgs = aCheckedArgs.getArray();
    sal_Int32 nProps = 0;
    bool  bWaitUntilEnd = false;
    sal_Int16 nDuplexMode = css::view::DuplexMode::UNKNOWN;
    for ( const beans::PropertyValue &rProp : rOptions )
    {
        // get Property-Value from options
        // FileName-Property?
        if ( rProp.Name == "FileName" )
        {
            // unpack th URL and check for a valid and well known protocol
            OUString sTemp;
            if (
                ( rProp.Value.getValueType()!=cppu::UnoType<OUString>::get())  ||
                (!(rProp.Value>>=sTemp))
               )
            {
                throw css::lang::IllegalArgumentException();
            }

            OUString      sPath;
            OUString      sURL  (sTemp);
            INetURLObject aCheck(sURL );
            if (aCheck.GetProtocol()==INetProtocol::NotValid)
            {
                // OK - it's not a valid URL. But may it's a simple
                // system path directly. It will be supported for historical
                // reasons. Otherwise we break too much external code...
                // We try to convert it to a file URL. If it's possible
                // we put the system path to the item set and let vcl work with it.
                // No ucb or thread will be necessary then. In case it couldn't be
                // converted it's not a URL nor a system path. Then we can't accept
                // this parameter and have to throw an exception.
                const OUString& sSystemPath(sTemp);
                OUString sFileURL;
                if (::osl::FileBase::getFileURLFromSystemPath(sSystemPath,sFileURL)!=::osl::FileBase::E_None)
                    throw css::lang::IllegalArgumentException();
                pCheckedArgs[nProps].Name = rProp.Name;
                pCheckedArgs[nProps++].Value <<= sFileURL;
                // and append the local filename
                aCheckedArgs.realloc( aCheckedArgs.getLength()+1 );
                pCheckedArgs = aCheckedArgs.getArray();
                pCheckedArgs[nProps].Name = "LocalFileName";
                pCheckedArgs[nProps++].Value <<= sTemp;
            }
            else
            // It's a valid URL. but now we must know, if it is a local one or not.
            // It's a question of using ucb or not!
            if (osl::FileBase::getSystemPathFromFileURL(sURL, sPath) == osl::FileBase::E_None)
            {
                // it's a local file, we can use vcl without special handling
                // And we have to use the system notation of the incoming URL.
                // But it into the descriptor and let the slot be executed at
                // the end of this method.
                pCheckedArgs[nProps].Name = rProp.Name;
                pCheckedArgs[nProps++].Value <<= sTemp;
                // and append the local filename
                aCheckedArgs.realloc( aCheckedArgs.getLength()+1 );
                pCheckedArgs = aCheckedArgs.getArray();
                pCheckedArgs[nProps].Name = "LocalFileName";
                pCheckedArgs[nProps++].Value <<= sPath;
            }
            else
            {
                // it's a ucb target. So we must use a temp. file for vcl
                // and move it after printing by using the ucb.
                // Create a temp file on the heap (because it must delete the
                // real file on disk automatically if it die - bt we have to share it with
                // some other sources ... e.g. the ImplUCBPrintWatcher).
                // And we put the name of this temp file to the descriptor instead
                // of the URL. The URL we save for later using separately.
                // Execution of the print job will be done later by executing
                // a slot ...
                if(!pUCBPrintTempFile)
                    pUCBPrintTempFile = new ::utl::TempFileNamed();
                pUCBPrintTempFile->EnableKillingFile();

                //FIXME: does it work?
                pCheckedArgs[nProps].Name = "LocalFileName";
                pCheckedArgs[nProps++].Value <<= pUCBPrintTempFile->GetFileName();
                sUcbUrl = sURL;
            }
        }

        // CopyCount-Property
        else if ( rProp.Name == "CopyCount" )
        {
            sal_Int32 nCopies = 0;
            if ( !( rProp.Value >>= nCopies ) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = rProp.Name;
            pCheckedArgs[nProps++].Value <<= nCopies;
        }

        // Collate-Property
        // Sort-Property (deprecated)
        else if ( rProp.Name == "Collate" || rProp.Name == "Sort" )
        {
            bool bTemp;
            if ( !(rProp.Value >>= bTemp) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = "Collate";
            pCheckedArgs[nProps++].Value <<= bTemp;
        }

        else if ( rProp.Name == "SinglePrintJobs" )
        {
            bool bTemp;
            if ( !(rProp.Value >>= bTemp) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = "SinglePrintJobs";
            pCheckedArgs[nProps++].Value <<= bTemp;
        }

        else if ( rProp.Name == "JobName" )
        {
            OUString sTemp;
            if( !(rProp.Value >>= sTemp) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = rProp.Name;
            pCheckedArgs[nProps++].Value <<= sTemp;
        }

        // Pages-Property
        else if ( rProp.Name == "Pages" )
        {
            OUString sTemp;
            if( !(rProp.Value >>= sTemp) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = rProp.Name;
            pCheckedArgs[nProps++].Value <<= sTemp;
        }

        // MonitorVisible
        else if ( rProp.Name == "MonitorVisible" )
        {
            if( !(rProp.Value >>= bMonitor) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = rProp.Name;
            pCheckedArgs[nProps++].Value <<= bMonitor;
        }

        // Wait
        else if ( rProp.Name == "Wait" )
        {
            if ( !(rProp.Value >>= bWaitUntilEnd) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = rProp.Name;
            pCheckedArgs[nProps++].Value <<= bWaitUntilEnd;
        }

        else if ( rProp.Name == "DuplexMode" )
        {
            if ( !(rProp.Value >>= nDuplexMode ) )
                throw css::lang::IllegalArgumentException();
            pCheckedArgs[nProps].Name = rProp.Name;
            pCheckedArgs[nProps++].Value <<= nDuplexMode;
        }
    }

    if ( nProps != aCheckedArgs.getLength() )
        aCheckedArgs.realloc(nProps);

    // Execute the print request every time.
    // It doesn't matter if it is a real printer used or we print to a local file
    // nor if we print to a temp file and move it afterwards by using the ucb.
    // That will be handled later. see pUCBPrintFile below!
    pView->ExecPrint( aCheckedArgs, true, false );

    // Ok - may be execution before has finished (or started!) printing.
    // And may it was a printing to a file.
    // Now we have to check if we can move the file (if necessary) via UCB to its right location.
    // Cases:
    //  a) printing finished                        => move the file directly and forget the watcher thread
    //  b) printing is asynchron and runs currently => start watcher thread and exit this method
    //                                                 This thread make all necessary things by itself.
    if (!pUCBPrintTempFile)
        return;

    // a)
    SfxPrinter* pPrinter = pView->GetPrinter();
    if ( ! pPrinter->IsPrinting() )
        ImplUCBPrintWatcher::moveAndDeleteTemp(&pUCBPrintTempFile,sUcbUrl);
    // b)
    else
    {
        // Note: we create(d) some resource on the heap (thread and temp file).
        // They will be deleted by the thread automatically if it finishes its run() method.
        ImplUCBPrintWatcher* pWatcher = new ImplUCBPrintWatcher( pPrinter, pUCBPrintTempFile, sUcbUrl );
        pWatcher->create();
    }
}

void IMPL_PrintListener_DataContainer::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    const SfxPrintingHint* pPrintHint = dynamic_cast<const SfxPrintingHint*>(&rHint);
    if ( &rBC != m_pObjectShell.get()
        || !pPrintHint
        || pPrintHint->GetWhich() == SFX_PRINTABLESTATE_CANCELJOB )
        return;

    if ( pPrintHint->GetWhich() == css::view::PrintableState_JOB_STARTED )
    {
        if ( !m_xPrintJob.is() )
            m_xPrintJob = new SfxPrintJob_Impl( this );
        m_aPrintOptions = pPrintHint->GetOptions();
    }

    std::unique_lock aGuard(m_aMutex);
    if (!m_aJobListeners.getLength(aGuard))
        return;
    view::PrintJobEvent aEvent;
    aEvent.Source = m_xPrintJob;
    aEvent.State = pPrintHint->GetWhich();

    comphelper::OInterfaceIteratorHelper4 pIterator(aGuard, m_aJobListeners);
    aGuard.unlock();
    while (pIterator.hasMoreElements())
        pIterator.next()->printJobEvent( aEvent );
}

void SAL_CALL SfxPrintHelper::addPrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener )
{
    std::unique_lock aGuard(m_pData->m_aMutex);
    m_pData->m_aJobListeners.addInterface( aGuard, xListener );
}

void SAL_CALL SfxPrintHelper::removePrintJobListener( const css::uno::Reference< css::view::XPrintJobListener >& xListener )
{
    std::unique_lock aGuard(m_pData->m_aMutex);
    m_pData->m_aJobListeners.removeInterface( aGuard, xListener );
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */