[Libreoffice-commits] core.git: Branch 'private/llunak/mailmerge_02' - 5 commits - config_host/config_cups.h.in configure.ac include/sfx2 include/vcl sfx2/source sw/source vcl/inc vcl/source vcl/unx

Luboš Luňák l.lunak at collabora.com
Thu Feb 26 15:19:50 PST 2015


 config_host/config_cups.h.in                   |    6 
 configure.ac                                   |    3 
 include/sfx2/viewsh.hxx                        |    5 
 include/vcl/jobdata.hxx                        |    6 
 include/vcl/print.hxx                          |    9 +
 include/vcl/printerinfomanager.hxx             |   10 +
 sfx2/source/view/viewprn.cxx                   |   15 ++
 sw/source/ui/dbui/dbmgr.cxx                    |  159 ++++++++++++++++++++-----
 sw/source/ui/inc/view.hxx                      |   11 -
 sw/source/ui/uno/unomailmerge.cxx              |    2 
 vcl/inc/cupsmgr.hxx                            |   23 +++
 vcl/source/gdi/print3.cxx                      |   73 ++++++-----
 vcl/unx/generic/printer/cupsmgr.cxx            |  113 ++++++++++++++---
 vcl/unx/generic/printer/jobdata.cxx            |   18 ++
 vcl/unx/generic/printer/printerinfomanager.cxx |   15 ++
 15 files changed, 378 insertions(+), 90 deletions(-)

New commits:
commit 7012d882fe7b8a4ea2104cbcf81cb019bd54efe9
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Thu Feb 26 15:56:24 2015 +0100

    support fast MM printing in non-single-file mode only for CUPS
    
    As said in the comment, the non-single-file mode could create way too many print
    jobs, so enable this only for the CUPS backend, which has been modified
    to send them as a single batch.
    
    Change-Id: I4c02ca0e8b91323b1d02f004c7b4813433064a11

diff --git a/config_host/config_cups.h.in b/config_host/config_cups.h.in
new file mode 100644
index 0000000..6794703
--- /dev/null
+++ b/config_host/config_cups.h.in
@@ -0,0 +1,6 @@
+#ifndef CONFIG_CUPS_H
+#define CONFIG_CUPS_H
+
+#define ENABLE_CUPS 0
+
+#endif
diff --git a/configure.ac b/configure.ac
index 0f492d2..bb5f111 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4348,7 +4348,7 @@ if test "$test_cups" = "yes"; then
     if test "$ac_cv_lib_cups_cupsPrintFiles" != "yes" -a "$ac_cv_header_cups_cups_h" != "yes"; then
         AC_MSG_ERROR([Could not find CUPS. Install libcupsys2-dev or cups-devel.])
     fi
-
+    AC_DEFINE(ENABLE_CUPS)
 else
     AC_MSG_RESULT([no])
 fi
@@ -12066,6 +12066,7 @@ fi
 AC_CONFIG_FILES([config_host.mk Makefile lo.xcent instsetoo_native/util/openoffice.lst])
 AC_CONFIG_HEADERS([config_host/config_buildid.h])
 AC_CONFIG_HEADERS([config_host/config_clang.h])
+AC_CONFIG_HEADERS([config_host/config_cups.h])
 AC_CONFIG_HEADERS([config_host/config_features.h])
 AC_CONFIG_HEADERS([config_host/config_gcc.h])
 AC_CONFIG_HEADERS([config_host/config_global.h])
diff --git a/include/vcl/jobdata.hxx b/include/vcl/jobdata.hxx
index f6fb94f..94c164f 100644
--- a/include/vcl/jobdata.hxx
+++ b/include/vcl/jobdata.hxx
@@ -77,6 +77,12 @@ struct VCL_DLLPUBLIC JobData
     static bool constructFromStreamBuffer( void* pData, int bytes, JobData& rJobData );
 };
 
+bool operator==(const psp::JobData& rLeft, const psp::JobData& rRight);
+inline bool operator!=(const psp::JobData& rLeft, const psp::JobData& rRight)
+{
+    return !( rLeft == rRight );
+}
+
 } // namespace
 
 
diff --git a/include/vcl/printerinfomanager.hxx b/include/vcl/printerinfomanager.hxx
index 108b315..d93f43d 100644
--- a/include/vcl/printerinfomanager.hxx
+++ b/include/vcl/printerinfomanager.hxx
@@ -229,6 +229,16 @@ public:
     // gets m_bDisableCUPS, initialized from printer config
     bool isCUPSDisabled() const;
 
+    // Starts printing in a batch mode, in which all printing will be done together instead of separate jobs.
+    // If the implementation supports it, calls to endSpool() will only delay the printing until flushBatchPrint()
+    // is called to print all delayed jobs.
+    // Returns false if failed or not supported (in which case endSpool() will print normally).
+    virtual bool startBatchPrint();
+    // Actually spools all delayed print jobs, if enabled, and disables batch mode.
+    virtual bool flushBatchPrint();
+    // Returns true batch printing is supported at all.
+    virtual bool supportsBatchPrint() const;
+
     virtual ~PrinterInfoManager();
 };
 
diff --git a/sw/source/ui/dbui/dbmgr.cxx b/sw/source/ui/dbui/dbmgr.cxx
index 50cce92..e263a1d 100644
--- a/sw/source/ui/dbui/dbmgr.cxx
+++ b/sw/source/ui/dbui/dbmgr.cxx
@@ -132,6 +132,9 @@
 #include <calc.hxx>
 #include <dbfld.hxx>
 
+#include <config_cups.h>
+#include <vcl/printerinfomanager.hxx>
+
 using namespace ::osl;
 using namespace ::svx;
 using namespace ::com::sun::star;
@@ -888,6 +891,27 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
     sal_Bool bNoError = sal_True;
     const bool bEMail = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL;
     const bool bMergeShell = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL;
+    bool bCreateSingleFile = rMergeDescriptor.bCreateSingleFile;
+
+    if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER )
+    {
+        // It is possible to do MM printing in both modes for the same result, but the singlefile mode
+        // is slower because of all the temporary document copies and merging them together
+        // into the single file, while the other mode simply updates fields and prints for every record.
+        // However, this would cause one print job for every record, and e.g. CUPS refuses new jobs
+        // if it has many jobs enqueued (500 by default), and with the current printing framework
+        // (which uses a pull model) it's rather complicated to create a single print job
+        // in steps.
+        // To handle this, CUPS backend has been changed to cache all the documents to print
+        // and send them to CUPS only as one job at the very end. Therefore, with CUPS, it's ok
+        // to use the faster mode. As I have no idea about other platforms, keep them using
+        // the slower singlefile mode (or feel free to check them, or rewrite the printing code).
+#if ENABLE_CUPS
+        bCreateSingleFile = !psp::PrinterInfoManager::get().supportsBatchPrint();
+#else
+        bCreateSingleFile = true;
+#endif
+    }
 
     ::rtl::Reference< MailDispatcher >          xMailDispatcher;
     OUString sBodyMimeType;
@@ -1006,7 +1030,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                     Application::Reschedule();
             }
 
-            if(rMergeDescriptor.bCreateSingleFile)
+            if(bCreateSingleFile)
             {
                 // create a target docshell to put the merged document into
                 xTargetDocShell = new SwDocShell( SFX_CREATE_MODE_STANDARD );
@@ -1105,7 +1129,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                     }
 
                     // create a new temporary file name - only done once in case of bCreateSingleFile
-                    if( createTempFile && ( 1 == nDocNo || !rMergeDescriptor.bCreateSingleFile ))
+                    if( createTempFile && ( 1 == nDocNo || !bCreateSingleFile ))
                     {
                         INetURLObject aEntry(sPath);
                         String sLeading;
@@ -1155,7 +1179,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                         // Create a copy of the source document and work with that one instead of the source.
                         // If we're not in the single file mode (which requires modifying the document for the merging),
                         // it is enough to do this just once.
-                        if( 1 == nDocNo || rMergeDescriptor.bCreateSingleFile )
+                        if( 1 == nDocNo || bCreateSingleFile )
                         {
                             assert( !xWorkDocSh.Is());
                             // copy the source document
@@ -1186,7 +1210,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                             rWorkShell.ViewShell::UpdateFlds();
                             SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE_FINISHED, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE_FINISHED), xWorkDocSh));
 
-                            if( rMergeDescriptor.bCreateSingleFile )
+                            if( bCreateSingleFile )
                                 pWorkDoc->RemoveInvisibleContent();
 
                             // launch MailMergeEvent if required
@@ -1198,7 +1222,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                                 pEvtSrc->LaunchMailMergeEvent( aEvt );
                             }
 
-                            if(rMergeDescriptor.bCreateSingleFile)
+                            if(bCreateSingleFile)
                             {
                                 OSL_ENSURE( pTargetShell, "no target shell available!" );
                                 // copy created file into the target document
@@ -1256,6 +1280,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                             }
                         else if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER )
                         {
+                            assert(!bCreateSingleFile);
                             if( 1 == nDocNo ) // set up printing only once at the beginning
                             {
                                 // printing should be done synchronously otherwise the document
@@ -1285,6 +1310,9 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                                 SfxPrinter* pDocPrt = pWorkView->GetPrinter(false);
                                 JobSetup aJobSetup = pDocPrt ? pDocPrt->GetJobSetup() : pWorkView->GetJobSetup();
                                 Printer::PreparePrintJob( pWorkView->GetPrinterController(), aJobSetup );
+#if ENABLE_CUPS
+                                psp::PrinterInfoManager::get().startBatchPrint();
+#endif
                             }
                             if( !Printer::ExecutePrintJob( pWorkView->GetPrinterController()))
                                 bCancel = true;
@@ -1396,7 +1424,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                                     }
                                 }
                             }
-                        if( rMergeDescriptor.bCreateSingleFile )
+                        if( bCreateSingleFile )
                         {
                             pWorkDoc->SetNewDBMgr( pOldDBManager );
                             xWorkDocSh->DoClose();
@@ -1409,7 +1437,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
 
                 // Freeze the layouts of the target document after the first inserted
                 // sub-document, to get the correct PageDesc.
-                if(!bFreezedLayouts && (rMergeDescriptor.bCreateSingleFile))
+                if(!bFreezedLayouts && bCreateSingleFile)
                 {
                     std::set<SwRootFrm*> aAllLayouts = pTargetShell->GetDoc()->GetAllLayouts();
                     std::for_each( aAllLayouts.begin(), aAllLayouts.end(),
@@ -1419,15 +1447,20 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
             } while( !bCancel &&
                 (bSynchronizedDoc && (nStartRow != nEndRow)? ExistsNextRecord() : ToNextMergeRecord()));
 
-            if( !rMergeDescriptor.bCreateSingleFile )
+            if( !bCreateSingleFile )
             {
                 if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER )
+                {
                     Printer::FinishPrintJob( pWorkView->GetPrinterController());
+#if ENABLE_CUPS
+                    psp::PrinterInfoManager::get().flushBatchPrint();
+#endif
+                }
                 pWorkDoc->SetNewDBMgr( pOldDBManager );
                 xWorkDocSh->DoClose();
             }
 
-            if (rMergeDescriptor.bCreateSingleFile)
+            if (bCreateSingleFile)
             {
                 // sw::DocumentLayoutManager::CopyLayoutFmt() did not generate
                 // unique fly names, do it here once.
@@ -1439,7 +1472,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                 Application::Reschedule();
 
             // Unfreeze target document layouts and correct all PageDescs.
-            if(rMergeDescriptor.bCreateSingleFile)
+            if(bCreateSingleFile)
             {
                 pTargetShell->CalcLayout();
                 std::set<SwRootFrm*> aAllLayouts = pTargetShell->GetDoc()->GetAllLayouts();
@@ -1455,7 +1488,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
             {
                 rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
             }
-            else if(rMergeDescriptor.bCreateSingleFile)
+            else if(bCreateSingleFile)
             {
                 if( rMergeDescriptor.nMergeType != DBMGR_MERGE_PRINTER )
                 {
diff --git a/vcl/inc/cupsmgr.hxx b/vcl/inc/cupsmgr.hxx
index 9a84b77..79c1ae7 100644
--- a/vcl/inc/cupsmgr.hxx
+++ b/vcl/inc/cupsmgr.hxx
@@ -58,6 +58,22 @@ class CUPSManager : public PrinterInfoManager
     osl::Mutex                                                  m_aGetPPDMutex;
     bool                                                        m_bPPDThreadRunning;
 
+    struct PendingJob
+    {
+        OUString printerName;
+        OUString jobTitle;
+        JobData jobData;
+        bool banner;
+        OString file;
+        PendingJob( const OUString& printerName_, const OUString& jobTitle_, const JobData& jobData_,
+            bool banner_, const OString& file_ )
+            : printerName( printerName_ ), jobTitle( jobTitle_ ), jobData( jobData_ ), banner( banner_ ), file( file_ )
+            {}
+        PendingJob() : banner( false ) {}
+    };
+    std::list< PendingJob > pendingJobs;
+    bool batchMode;
+
     CUPSManager();
     virtual ~CUPSManager();
 
@@ -66,6 +82,9 @@ class CUPSManager : public PrinterInfoManager
     void getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions ) const;
     void runDests();
     OString threadedCupsGetPPD(const char* pPrinter);
+
+    bool processPendingJobs();
+    bool printJobs( const PendingJob& job, const std::vector< OString >& files );
 public:
     // public for stub
     static void runDestThread(void* pMgr);
@@ -81,6 +100,10 @@ public:
     virtual int endSpool( const OUString& rPrinterName, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner );
     virtual void setupJobContextData( JobData& rData );
 
+    virtual bool startBatchPrint() SAL_OVERRIDE;
+    virtual bool flushBatchPrint() SAL_OVERRIDE;
+    virtual bool supportsBatchPrint() const SAL_OVERRIDE;
+
     // changes the info about a named printer
     virtual void changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo );
 
diff --git a/vcl/unx/generic/printer/cupsmgr.cxx b/vcl/unx/generic/printer/cupsmgr.cxx
index 6b06596..5169d29 100644
--- a/vcl/unx/generic/printer/cupsmgr.cxx
+++ b/vcl/unx/generic/printer/cupsmgr.cxx
@@ -646,8 +646,6 @@ int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTit
                rDocumentJobData.m_nCopies
                );
 
-    int nJobID = 0;
-
     osl::MutexGuard aGuard( m_aCUPSMutex );
 
     boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
@@ -659,25 +657,98 @@ int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTit
     }
 
     boost::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile );
-    if( it != m_aSpoolFiles.end() )
+    if( it == m_aSpoolFiles.end() )
+        return false;
+    fclose( pFile );
+    PendingJob job( rPrintername, rJobTitle, rDocumentJobData, bBanner, it->second );
+    m_aSpoolFiles.erase( pFile );
+    pendingJobs.push_back( job );
+    if( !batchMode ) // process immediately, otherwise will be handled by flushBatchPrint()
+        return processPendingJobs();
+    return true;
+}
+
+bool CUPSManager::startBatchPrint()
+{
+    batchMode = true;
+    return true;
+}
+
+bool CUPSManager::supportsBatchPrint() const
+{
+    return true;
+}
+
+bool CUPSManager::flushBatchPrint()
+{
+    osl::MutexGuard aGuard( m_aCUPSMutex );
+    return processPendingJobs();
+}
+
+bool CUPSManager::processPendingJobs()
+{
+    // Print all jobs that have the same data using one CUPS call (i.e. merge all jobs that differ only in files to print).
+    PendingJob currentJobData;
+    bool first = true;
+    std::vector< OString > files;
+    bool ok = true;
+    while( !pendingJobs.empty())
+    {
+        if( first )
+        {
+            currentJobData = pendingJobs.front();
+            first = false;
+        }
+        else if( currentJobData.printerName != pendingJobs.front().printerName
+                || currentJobData.jobTitle != pendingJobs.front().jobTitle
+                || currentJobData.jobData != pendingJobs.front().jobData
+                || currentJobData.banner != pendingJobs.front().banner )
+        {
+            if( !printJobs( currentJobData, files ))
+                ok = false;
+            files.clear();
+            currentJobData = pendingJobs.front();
+        }
+        files.push_back( pendingJobs.front().file );
+        pendingJobs.pop_front();
+    }
+    if( !first )
     {
-        fclose( pFile );
-        rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
+        if( !printJobs( currentJobData, files )) // print the last batch
+            ok = false;
+    }
+    return ok;
+}
 
-        // setup cups options
-        int nNumOptions = 0;
-        cups_option_t* pOptions = NULL;
-        getOptionsFromDocumentSetup( rDocumentJobData, bBanner, nNumOptions, (void**)&pOptions );
+bool CUPSManager::printJobs( const PendingJob& job, const std::vector< OString >& files )
+{
+    boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it =
+        m_aCUPSDestMap.find( job.printerName );
+
+    rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
+
+    // setup cups options
+    int nNumOptions = 0;
+    cups_option_t* pOptions = NULL;
+    getOptionsFromDocumentSetup( job.jobData, job.banner, nNumOptions, reinterpret_cast<void**>(&pOptions) );
+
+    cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second;
 
-        cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second;
-        nJobID = cupsPrintFile( pDest->name,
-        it->second.getStr(),
-        OUStringToOString( rJobTitle, aEnc ).getStr(),
-        nNumOptions, pOptions );
+    std::vector< const char* > fnames;
+    for( std::vector< OString >::const_iterator it = files.begin();
+         it != files.end();
+         ++it )
+        fnames.push_back( it->getStr());
+
+    int nJobID = cupsPrintFiles(pDest->name,
+            fnames.size(),
+            fnames.data(),
+            OUStringToOString( job.jobTitle, aEnc ).getStr(),
+            nNumOptions, pOptions);
 #if OSL_DEBUG_LEVEL > 1
         fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n",
                     pDest->name,
-                    it->second.getStr(),
+                    ( fnames.size() == 1 ? files.front() : OString::number( fnames.size()) ).getStr(),
                     OUStringToOString( rJobTitle, aEnc ).getStr(),
                     nNumOptions,
                     pOptions,
@@ -691,11 +762,13 @@ int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTit
         system( aCmd.getStr() );
 #endif
 
-        unlink( it->second.getStr() );
-        m_aSpoolFiles.erase( pFile );
-        if( pOptions )
-            cupsFreeOptions( nNumOptions, pOptions );
-    }
+    for( std::vector< OString >::const_iterator it = files.begin();
+         it != files.end();
+         ++it )
+        unlink( it->getStr());
+
+    if( pOptions )
+        cupsFreeOptions( nNumOptions, pOptions );
 
     return nJobID;
 }
diff --git a/vcl/unx/generic/printer/jobdata.cxx b/vcl/unx/generic/printer/jobdata.cxx
index d40fffd..16adb96 100644
--- a/vcl/unx/generic/printer/jobdata.cxx
+++ b/vcl/unx/generic/printer/jobdata.cxx
@@ -52,6 +52,24 @@ JobData& JobData::operator=(const JobData& rRight)
     return *this;
 }
 
+bool psp::operator==(const psp::JobData& rLeft, const psp::JobData& rRight)
+{
+    return rLeft.m_nCopies == rRight.m_nCopies
+//        && rLeft.m_bCollate == rRight.m_bCollate
+        && rLeft.m_nLeftMarginAdjust == rRight.m_nLeftMarginAdjust
+        && rLeft.m_nRightMarginAdjust == rRight.m_nRightMarginAdjust
+        && rLeft.m_nTopMarginAdjust == rRight.m_nTopMarginAdjust
+        && rLeft.m_nBottomMarginAdjust == rRight.m_nBottomMarginAdjust
+        && rLeft.m_nColorDepth == rRight.m_nColorDepth
+        && rLeft.m_eOrientation == rRight.m_eOrientation
+        && rLeft.m_aPrinterName == rRight.m_aPrinterName
+        && rLeft.m_pParser == rRight.m_pParser
+//        && rLeft.m_aContext == rRight.m_aContext
+        && rLeft.m_nPSLevel == rRight.m_nPSLevel
+        && rLeft.m_nPDFDevice == rRight.m_nPDFDevice
+        && rLeft.m_nColorDevice == rRight.m_nColorDevice;
+}
+
 void JobData::setCollate( bool bCollate )
 {
     const PPDParser* pParser = m_aContext.getParser();
diff --git a/vcl/unx/generic/printer/printerinfomanager.cxx b/vcl/unx/generic/printer/printerinfomanager.cxx
index 7def80f..b895f82 100644
--- a/vcl/unx/generic/printer/printerinfomanager.cxx
+++ b/vcl/unx/generic/printer/printerinfomanager.cxx
@@ -1180,6 +1180,21 @@ void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const
 
 // -----------------------------------------------------------------
 
+bool PrinterInfoManager::startBatchPrint()
+{
+    return false; // not implemented
+}
+
+bool PrinterInfoManager::supportsBatchPrint() const
+{
+    return false;
+}
+
+bool PrinterInfoManager::flushBatchPrint()
+{
+    return false;
+}
+
 SystemQueueInfo::SystemQueueInfo() :
     m_bChanged( false )
 {
commit a3dae6b5b63687ed1dfde476dc826f1f3eff23ab
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Thu Feb 19 16:39:06 2015 +0100

    mailmerge doesn't need to use the singlefile technique for printing
    
    It should be sufficient for every record to just update the fields and print
    the document. The printing code apparently rather expects to do the printing
    in one go, so split the functions up there.
    
    Change-Id: I4d17c703b3220f47609fc4b054ce048b299a0c92

diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx
index ad4c64c..57d067d 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -36,6 +36,7 @@
 #include <tools/errcode.hxx>
 #include <vcl/jobset.hxx>
 #include <vector>
+#include <boost/shared_ptr.hpp>
 
 class SfxBaseController;
 class Size;
@@ -59,6 +60,7 @@ class SfxFrameItem;
 class Dialog;
 class Menu;
 class NotifyEvent;
+namespace vcl { class PrinterController; }
 
 #define SFX_PRINTER_PRINTER               1  // without JOB SETUP => Temporary
 #define SFX_PRINTER_JOBSETUP         2
@@ -257,6 +259,9 @@ public:
     sal_Bool                        TryContextMenuInterception( Menu& rIn, const OUString& rMenuIdentifier, Menu*& rpOut, ::com::sun::star::ui::ContextMenuExecuteEvent aEvent );
 
     void                        ExecPrint( const com::sun::star::uno::Sequence < com::sun::star::beans::PropertyValue >&, sal_Bool, sal_Bool );
+    // Like ExecPrint(), but only sets up for printing. Use Printer::ExecutePrintJob() and Printer::FinishPrintJob() afterwards.
+    void                        StartPrint( const com::sun::star::uno::Sequence < com::sun::star::beans::PropertyValue >&, bool, bool );
+    boost::shared_ptr< vcl::PrinterController > GetPrinterController() const;
 
     void                        AddRemoveClipboardListener( const com::sun::star::uno::Reference < com::sun::star::datatransfer::clipboard::XClipboardListener>&, sal_Bool );
     ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardNotifier > GetClipboardNotifier();
diff --git a/include/vcl/print.hxx b/include/vcl/print.hxx
index 40997dd..9ab7344 100644
--- a/include/vcl/print.hxx
+++ b/include/vcl/print.hxx
@@ -373,9 +373,16 @@ public:
                           const JobSetup& i_rInitSetup
                           );
 
+    // These 3 together are more modular PrintJob(), allowing printing more documents as one print job
+    // by repeated calls to ExecutePrintJob(). Used by mailmerge.
+    static void                 PreparePrintJob( boost::shared_ptr<vcl::PrinterController> i_pController,
+                                        const JobSetup& i_rInitSetup );
+    static bool                 ExecutePrintJob( boost::shared_ptr<vcl::PrinterController> i_pController );
+    static void                 FinishPrintJob( boost::shared_ptr<vcl::PrinterController> i_pController );
+
     // implementation detail of PrintJob being asynchronous
     // not exported, not usable outside vcl
-    static void SAL_DLLPRIVATE ImplPrintJob( const boost::shared_ptr<vcl::PrinterController>& i_pController,
+    static void SAL_DLLPRIVATE ImplPrintJob( boost::shared_ptr<vcl::PrinterController> i_pController,
                                              const JobSetup& i_rInitSetup
                                              );
 };
diff --git a/sfx2/source/view/viewprn.cxx b/sfx2/source/view/viewprn.cxx
index 251dbb0..27b720d 100644
--- a/sfx2/source/view/viewprn.cxx
+++ b/sfx2/source/view/viewprn.cxx
@@ -579,8 +579,10 @@ SfxPrinter* SfxViewShell::SetPrinter_Impl( SfxPrinter *pNewPrinter )
     return pDocPrinter;
 }
 
-void SfxViewShell::ExecPrint( const uno::Sequence < beans::PropertyValue >& rProps, sal_Bool bIsAPI, sal_Bool bIsDirect )
+void SfxViewShell::StartPrint( const uno::Sequence < beans::PropertyValue >& rProps, bool bIsAPI, bool bIsDirect )
 {
+    assert( pImp->m_xPrinterController.get() == NULL );
+
     // get the current selection; our controller should know it
     Reference< frame::XController > xController( GetController() );
     Reference< view::XSelectionSupplier > xSupplier( xController, UNO_QUERY );
@@ -622,7 +624,11 @@ void SfxViewShell::ExecPrint( const uno::Sequence < beans::PropertyValue >& rPro
     SfxObjectShell *pObjShell = GetObjectShell();
     pController->setValue( OUString( "JobName"  ),
                         makeAny( OUString( pObjShell->GetTitle(0) ) ) );
+}
 
+void SfxViewShell::ExecPrint( const uno::Sequence < beans::PropertyValue >& rProps, sal_Bool bIsAPI, sal_Bool bIsDirect )
+{
+    StartPrint( rProps, bIsAPI, bIsDirect );
     // FIXME: job setup
     SfxPrinter* pDocPrt = GetPrinter(sal_False);
     JobSetup aJobSetup = pDocPrt ? pDocPrt->GetJobSetup() : GetJobSetup();
@@ -630,7 +636,12 @@ void SfxViewShell::ExecPrint( const uno::Sequence < beans::PropertyValue >& rPro
         aJobSetup.SetValue( String( "IsQuickJob"  ),
                             String( "true"  ) );
 
-    Printer::PrintJob( pController, aJobSetup );
+    Printer::PrintJob( GetPrinterController(), aJobSetup );
+}
+
+boost::shared_ptr< vcl::PrinterController > SfxViewShell::GetPrinterController() const
+{
+    return pImp->m_pPrinterController;
 }
 
 Printer* SfxViewShell::GetActivePrinter() const
diff --git a/sw/source/ui/dbui/dbmgr.cxx b/sw/source/ui/dbui/dbmgr.cxx
index ca697eb..50cce92 100644
--- a/sw/source/ui/dbui/dbmgr.cxx
+++ b/sw/source/ui/dbui/dbmgr.cxx
@@ -23,6 +23,7 @@
 #include <unotxdoc.hxx>
 #include <com/sun/star/text/NotePrintMode.hpp>
 #include <sfx2/app.hxx>
+#include <sfx2/printer.hxx>
 #include <com/sun/star/sdb/CommandType.hpp>
 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
 #include <com/sun/star/frame/XComponentLoader.hpp>
@@ -1253,6 +1254,41 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                                     rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
                                 }
                             }
+                        else if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER )
+                        {
+                            if( 1 == nDocNo ) // set up printing only once at the beginning
+                            {
+                                // printing should be done synchronously otherwise the document
+                                // might already become invalid during the process
+                                uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
+
+                                aOptions.realloc( 2 );
+                                aOptions[ 0 ].Name = "Wait";
+                                aOptions[ 0 ].Value <<= sal_True;
+                                aOptions[ 1 ].Name = "MonitorVisible";
+                                aOptions[ 1 ].Value <<= sal_False;
+                                // move print options
+                                const beans::PropertyValue* pPrintOptions = rMergeDescriptor.aPrintOptions.getConstArray();
+                                for( sal_Int32 nOption = 0, nIndex = 1 ; nOption < rMergeDescriptor.aPrintOptions.getLength(); ++nOption)
+                                {
+                                    if( pPrintOptions[nOption].Name == "CopyCount" || pPrintOptions[nOption].Name == "FileName"
+                                        || pPrintOptions[nOption].Name == "Collate" || pPrintOptions[nOption].Name == "Pages"
+                                        || pPrintOptions[nOption].Name == "Wait" || pPrintOptions[nOption].Name == "PrinterName" )
+                                    {
+                                        // add an option
+                                        aOptions.realloc( nIndex + 1 );
+                                        aOptions[ nIndex ].Name = pPrintOptions[nOption].Name;
+                                        aOptions[ nIndex++ ].Value = pPrintOptions[nOption].Value ;
+                                    }
+                                }
+                                pWorkView->StartPrint( aOptions, IsMergeSilent(), rMergeDescriptor.bPrintAsync );
+                                SfxPrinter* pDocPrt = pWorkView->GetPrinter(false);
+                                JobSetup aJobSetup = pDocPrt ? pDocPrt->GetJobSetup() : pWorkView->GetJobSetup();
+                                Printer::PreparePrintJob( pWorkView->GetPrinterController(), aJobSetup );
+                            }
+                            if( !Printer::ExecutePrintJob( pWorkView->GetPrinterController()))
+                                bCancel = true;
+                        }
                             else
                             {
                                 assert( createTempFile );
@@ -1385,6 +1421,8 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
 
             if( !rMergeDescriptor.bCreateSingleFile )
             {
+                if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER )
+                    Printer::FinishPrintJob( pWorkView->GetPrinterController());
                 pWorkDoc->SetNewDBMgr( pOldDBManager );
                 xWorkDocSh->DoClose();
             }
@@ -2704,7 +2742,7 @@ void SwNewDBMgr::ExecuteFormLetter( SwWrtShell& rSh,
 
                     SwMergeDescriptor aMergeDesc( pImpl->pMergeDialog->GetMergeType(), pView->GetWrtShell(), aDescriptor );
                     aMergeDesc.sSaveToFilter = pImpl->pMergeDialog->GetSaveFilter();
-                    aMergeDesc.bCreateSingleFile = pImpl->pMergeDialog->IsSaveSingleDoc();
+                    aMergeDesc.bCreateSingleFile = pImpl->pMergeDialog->IsSaveSingleDoc() && pImpl->pMergeDialog->GetMergeType() != DBMGR_MERGE_PRINTER;
                     aMergeDesc.bSubjectIsFilename = aMergeDesc.bCreateSingleFile;
                     if( !aMergeDesc.bCreateSingleFile && pImpl->pMergeDialog->IsGenerateFromDataBase() )
                     {
diff --git a/sw/source/ui/uno/unomailmerge.cxx b/sw/source/ui/uno/unomailmerge.cxx
index c58e1dd..bffd112 100644
--- a/sw/source/ui/uno/unomailmerge.cxx
+++ b/sw/source/ui/uno/unomailmerge.cxx
@@ -718,7 +718,7 @@ uno::Any SAL_CALL SwXMailMerge::execute(
             // when mail merge is called as command line macro
             aMergeDesc.bPrintAsync = sal_False;
             aMergeDesc.aPrintOptions = aPrintSettings;
-            aMergeDesc.bCreateSingleFile = sal_True;
+            aMergeDesc.bCreateSingleFile = sal_False;
         }
         break;
     case MailMergeType::SHELL:
diff --git a/vcl/source/gdi/print3.cxx b/vcl/source/gdi/print3.cxx
index cb534c8..dc94cd4 100644
--- a/vcl/source/gdi/print3.cxx
+++ b/vcl/source/gdi/print3.cxx
@@ -302,12 +302,10 @@ void Printer::PrintJob( const boost::shared_ptr<PrinterController>& i_pControlle
     }
 }
 
-void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pController,
+void Printer::PreparePrintJob( boost::shared_ptr<PrinterController> pController,
                             const JobSetup& i_rInitSetup
                             )
 {
-    boost::shared_ptr<PrinterController> pController( i_pController );
-
     // check if there is a default printer; if not, show an error box (if appropriate)
     if( GetDefaultPrinterName().isEmpty() )
     {
@@ -335,7 +333,7 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
     }
 
     // reset last page property
-    i_pController->setLastPage( sal_False );
+    pController->setLastPage( sal_False );
 
     // update "PageRange" property inferring from other properties:
     // case 1: "Pages" set from UNO API ->
@@ -347,12 +345,12 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
     // "Pages" attribute from API is now equivalent to "PageRange"
     // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
     // Argh ! That sure needs cleaning up
-    beans::PropertyValue* pContentVal = i_pController->getValue( OUString( "PrintRange" ) );
+    beans::PropertyValue* pContentVal = pController->getValue( OUString( "PrintRange" ) );
     if( ! pContentVal )
-        pContentVal = i_pController->getValue( OUString( "PrintContent" ) );
+        pContentVal = pController->getValue( OUString( "PrintContent" ) );
 
     // case 1: UNO API has set "Pages"
-    beans::PropertyValue* pPagesVal = i_pController->getValue( OUString( "Pages" ) );
+    beans::PropertyValue* pPagesVal = pController->getValue( OUString( "Pages" ) );
     if( pPagesVal )
     {
         OUString aPagesVal;
@@ -365,7 +363,7 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
             if( pContentVal )
             {
                 pContentVal->Value = makeAny( sal_Int32( 1 ) );
-                i_pController->setValue( OUString( "PageRange" ), pPagesVal->Value );
+                pController->setValue( OUString( "PageRange" ), pPagesVal->Value );
             }
         }
     }
@@ -378,13 +376,13 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
             if( nContent == 0 )
             {
                 // do not overwrite PageRange if it is already set
-                beans::PropertyValue* pRangeVal = i_pController->getValue( OUString( "PageRange" ) );
+                beans::PropertyValue* pRangeVal = pController->getValue( OUString( "PageRange" ) );
                 OUString aRange;
                 if( pRangeVal )
                     pRangeVal->Value >>= aRange;
                 if( aRange.isEmpty() )
                 {
-                    sal_Int32 nPages = i_pController->getPageCount();
+                    sal_Int32 nPages = pController->getPageCount();
                     if( nPages > 0 )
                     {
                         OUStringBuffer aBuf( 32 );
@@ -394,14 +392,14 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
                             aBuf.appendAscii( "-" );
                             aBuf.append( nPages );
                         }
-                        i_pController->setValue( OUString( "PageRange" ), makeAny( aBuf.makeStringAndClear() ) );
+                        pController->setValue( OUString( "PageRange" ), makeAny( aBuf.makeStringAndClear() ) );
                     }
                 }
             }
         }
     }
 
-    beans::PropertyValue* pReverseVal = i_pController->getValue( OUString( "PrintReverse" ) );
+    beans::PropertyValue* pReverseVal = pController->getValue( OUString( "PrintReverse" ) );
     if( pReverseVal )
     {
         sal_Bool bReverse = sal_False;
@@ -409,7 +407,7 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
         pController->setReversePrint( bReverse );
     }
 
-    beans::PropertyValue* pPapersizeFromSetupVal = i_pController->getValue( OUString( "PapersizeFromSetup" ) );
+    beans::PropertyValue* pPapersizeFromSetupVal = pController->getValue( OUString( "PapersizeFromSetup" ) );
     if( pPapersizeFromSetupVal )
     {
         sal_Bool bPapersizeFromSetup = sal_False;
@@ -418,35 +416,35 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
     }
 
     // setup NUp printing from properties
-    sal_Int32 nRows = i_pController->getIntProperty( OUString( "NUpRows" ), 1 );
-    sal_Int32 nCols = i_pController->getIntProperty( OUString( "NUpColumns" ), 1 );
+    sal_Int32 nRows = pController->getIntProperty( OUString( "NUpRows" ), 1 );
+    sal_Int32 nCols = pController->getIntProperty( OUString( "NUpColumns" ), 1 );
     if( nRows > 1 || nCols > 1 )
     {
         PrinterController::MultiPageSetup aMPS;
         aMPS.nRows         = nRows > 1 ? nRows : 1;
         aMPS.nColumns      = nCols > 1 ? nCols : 1;
-        sal_Int32 nValue = i_pController->getIntProperty( OUString( "NUpPageMarginLeft" ), aMPS.nLeftMargin );
+        sal_Int32 nValue = pController->getIntProperty( OUString( "NUpPageMarginLeft" ), aMPS.nLeftMargin );
         if( nValue >= 0 )
             aMPS.nLeftMargin = nValue;
-        nValue = i_pController->getIntProperty( OUString( "NUpPageMarginRight" ), aMPS.nRightMargin );
+        nValue = pController->getIntProperty( OUString( "NUpPageMarginRight" ), aMPS.nRightMargin );
         if( nValue >= 0 )
             aMPS.nRightMargin = nValue;
-        nValue = i_pController->getIntProperty( OUString( "NUpPageMarginTop" ), aMPS.nTopMargin );
+        nValue = pController->getIntProperty( OUString( "NUpPageMarginTop" ), aMPS.nTopMargin );
         if( nValue >= 0 )
             aMPS.nTopMargin = nValue;
-        nValue = i_pController->getIntProperty( OUString( "NUpPageMarginBottom" ), aMPS.nBottomMargin );
+        nValue = pController->getIntProperty( OUString( "NUpPageMarginBottom" ), aMPS.nBottomMargin );
         if( nValue >= 0 )
             aMPS.nBottomMargin = nValue;
-        nValue = i_pController->getIntProperty( OUString( "NUpHorizontalSpacing" ), aMPS.nHorizontalSpacing );
+        nValue = pController->getIntProperty( OUString( "NUpHorizontalSpacing" ), aMPS.nHorizontalSpacing );
         if( nValue >= 0 )
             aMPS.nHorizontalSpacing = nValue;
-        nValue = i_pController->getIntProperty( OUString( "NUpVerticalSpacing" ), aMPS.nVerticalSpacing );
+        nValue = pController->getIntProperty( OUString( "NUpVerticalSpacing" ), aMPS.nVerticalSpacing );
         if( nValue >= 0 )
             aMPS.nVerticalSpacing = nValue;
-        aMPS.bDrawBorder = i_pController->getBoolProperty( OUString( "NUpDrawBorder" ), aMPS.bDrawBorder );
-        aMPS.nOrder = static_cast<PrinterController::NupOrderType>(i_pController->getIntProperty( OUString( "NUpSubPageOrder" ), aMPS.nOrder ));
-        aMPS.aPaperSize = i_pController->getPrinter()->PixelToLogic( i_pController->getPrinter()->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) );
-        beans::PropertyValue* pPgSizeVal = i_pController->getValue( OUString( "NUpPaperSize" ) );
+        aMPS.bDrawBorder = pController->getBoolProperty( OUString( "NUpDrawBorder" ), aMPS.bDrawBorder );
+        aMPS.nOrder = static_cast<PrinterController::NupOrderType>(pController->getIntProperty( OUString( "NUpSubPageOrder" ), aMPS.nOrder ));
+        aMPS.aPaperSize = pController->getPrinter()->PixelToLogic( pController->getPrinter()->GetPaperSizePixel(), MapMode( MAP_100TH_MM ) );
+        beans::PropertyValue* pPgSizeVal = pController->getValue( OUString( "NUpPaperSize" ) );
         awt::Size aSizeVal;
         if( pPgSizeVal && (pPgSizeVal->Value >>= aSizeVal) )
         {
@@ -454,7 +452,7 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
             aMPS.aPaperSize.Height() = aSizeVal.Height;
         }
 
-        i_pController->setMultipage( aMPS );
+        pController->setMultipage( aMPS );
     }
 
     // in direct print case check whether there is anything to print.
@@ -478,10 +476,10 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
     {
         try
         {
-            PrintDialog aDlg( NULL, i_pController );
+            PrintDialog aDlg( NULL, pController );
             if( ! aDlg.Execute() )
             {
-                i_pController->abortJob();
+                pController->abortJob();
                 return;
             }
             if( aDlg.isPrintToFile() )
@@ -489,7 +487,7 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
                 OUString aFile = queryFile( pController->getPrinter().get() );
                 if( aFile.isEmpty() )
                 {
-                    i_pController->abortJob();
+                    pController->abortJob();
                     return;
                 }
                 pController->setValue( OUString( "LocalFileName" ),
@@ -507,19 +505,32 @@ void Printer::ImplPrintJob( const boost::shared_ptr<PrinterController>& i_pContr
     }
 
     pController->pushPropertiesToPrinter();
+}
 
+bool Printer::ExecutePrintJob(boost::shared_ptr<PrinterController> pController)
+{
     OUString aJobName;
     beans::PropertyValue* pJobNameVal = pController->getValue( OUString( "JobName" ) );
     if( pJobNameVal )
         pJobNameVal->Value >>= aJobName;
 
-    pController->getPrinter()->StartJob( String( aJobName ), pController );
+    return pController->getPrinter()->StartJob( aJobName, pController );
+}
 
+void Printer::FinishPrintJob(boost::shared_ptr<PrinterController> pController)
+{
     pController->resetPaperToLastConfigured();
-
     pController->jobFinished( pController->getJobState() );
 }
 
+void Printer::ImplPrintJob(boost::shared_ptr<PrinterController> xController,
+                           const JobSetup& i_rInitSetup)
+{
+    PreparePrintJob( xController, i_rInitSetup );
+    ExecutePrintJob( xController );
+    FinishPrintJob( xController );
+}
+
 bool Printer::StartJob( const OUString& i_rJobName, boost::shared_ptr<vcl::PrinterController>& i_pController )
 {
     mnError = PRINTER_OK;
commit 5a6655ef9d02b9cb5d504d2df6337654c278c151
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Tue Feb 17 21:31:21 2015 +0100

    do not needlessly make overriden functions more private
    
    These are public in the base class.
    
    Change-Id: I839ffe56a835f99c2812cffb60804b74aaa9c5ac

diff --git a/sw/source/ui/inc/view.hxx b/sw/source/ui/inc/view.hxx
index 056ae0d..4a42580 100644
--- a/sw/source/ui/inc/view.hxx
+++ b/sw/source/ui/inc/view.hxx
@@ -336,11 +336,6 @@ class SW_DLLPUBLIC SwView: public SfxViewShell
     SW_DLLPRIVATE void          SpellKontext(sal_Bool bOn = sal_True)
                             { m_bCenterCrsr = bOn; m_bAlwaysShowSel = bOn; }
 
-    // methods for printing
-    SW_DLLPRIVATE virtual   SfxPrinter*     GetPrinter( sal_Bool bCreate = sal_False );
-    SW_DLLPRIVATE virtual bool  HasPrintOptionsPage() const;
-    SW_DLLPRIVATE virtual SfxTabPage*       CreatePrintOptionsPage( Window* pParent,
-                                                    const SfxItemSet& rSet);
     // for readonly switching
     SW_DLLPRIVATE virtual void  Notify( SfxBroadcaster& rBC, const SfxHint& rHint );
     SW_DLLPRIVATE void          _CheckReadonlyState();
@@ -663,6 +658,12 @@ public:
     void UpdateDocStats();
     /// Where is the real cursor: in the annotation or in the main document?
     void SetAnnotationMode(bool bMode);
+
+    // methods for printing
+    SW_DLLPRIVATE virtual   SfxPrinter*     GetPrinter( sal_Bool bCreate = sal_False );
+    SW_DLLPRIVATE virtual bool  HasPrintOptionsPage() const;
+    SW_DLLPRIVATE virtual SfxTabPage*       CreatePrintOptionsPage( Window* pParent,
+                                                    const SfxItemSet& rSet);
 };
 
 // ----------------- inline Methoden ----------------------
commit 5a7bb3f0484654fd0499e9bb030c83284e0d6571
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Sun Feb 15 22:53:57 2015 +0100

    avoid creating temporary document copies in non-singlefile MM mode
    
    The singlefile mode needs a new copy for every record, because it modifies
    it (such as changing fields to text), but the non-singlefile mode does not
    need all that relatively expensive work, because it just updates the fields
    and nothing else.
    
    Change-Id: If02cf8aca1d0f050ffb63cd85d5a9455afc5a6ea

diff --git a/sw/source/ui/dbui/dbmgr.cxx b/sw/source/ui/dbui/dbmgr.cxx
index 79ebd0d..ca697eb 100644
--- a/sw/source/ui/dbui/dbmgr.cxx
+++ b/sw/source/ui/dbui/dbmgr.cxx
@@ -1077,6 +1077,14 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
             bool bFreezedLayouts = false;
             // collect temporary files
             ::std::vector< String> aFilesToRemove;
+
+            // The SfxObjectShell will be closed explicitly later but it is more safe to use SfxObjectShellLock here
+            SfxObjectShellLock xWorkDocSh;
+            // a view frame for the document
+            SwView* pWorkView = NULL;
+            SwDoc* pWorkDoc = NULL;
+            SwNewDBMgr* pOldDBManager = NULL;
+
             do
             {
                 nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;
@@ -1143,21 +1151,26 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                         for( sal_uInt16 i = 0; i < 25; i++ )
                             Application::Reschedule();
 
-                        // The SfxObjectShell will be closed explicitly later but it is more safe to use SfxObjectShellLock here
-                        // copy the source document
-                        SfxObjectShellLock xWorkDocSh = pSourceDocSh->GetDoc()->CreateCopy( true );
+                        // Create a copy of the source document and work with that one instead of the source.
+                        // If we're not in the single file mode (which requires modifying the document for the merging),
+                        // it is enough to do this just once.
+                        if( 1 == nDocNo || rMergeDescriptor.bCreateSingleFile )
+                        {
+                            assert( !xWorkDocSh.Is());
+                            // copy the source document
+                            xWorkDocSh = pSourceDocSh->GetDoc()->CreateCopy( true );
 
                             //create a view frame for the document
-                            SwView* pWorkView = static_cast< SwView* >( SfxViewFrame::LoadHiddenDocument( *xWorkDocSh, 0 )->GetViewShell() );
+                            pWorkView = static_cast< SwView* >( SfxViewFrame::LoadHiddenDocument( *xWorkDocSh, 0 )->GetViewShell() );
                             //request the layout calculation
                             SwWrtShell& rWorkShell = pWorkView->GetWrtShell();
                             pWorkView->AttrChangedNotify( &rWorkShell );// in order for SelectShell to be called
 
-                            SwDoc* pWorkDoc = rWorkShell.GetDoc();
+                            pWorkDoc = rWorkShell.GetDoc();
                             lcl_CopyDocumentPorperties( xSourceDocProps, xWorkDocSh, pWorkDoc );
                             if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                                 lcl_SaveDoc( xWorkDocSh, "WorkDoc", nDocNo );
-                            SwNewDBMgr* pOldDBMgr = pWorkDoc->GetNewDBMgr();
+                            pOldDBManager = pWorkDoc->GetNewDBMgr();
                             pWorkDoc->SetNewDBMgr( this );
                             pWorkDoc->EmbedAllLinks();
 
@@ -1165,12 +1178,15 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                             rWorkShell.LockExpFlds();
                             rWorkShell.CalcLayout();
                             rWorkShell.UnlockExpFlds();
+                        }
 
+                            SwWrtShell& rWorkShell = pWorkView->GetWrtShell();
                             SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE), xWorkDocSh));
                             rWorkShell.ViewShell::UpdateFlds();
                             SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE_FINISHED, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE_FINISHED), xWorkDocSh));
 
-                            pWorkDoc->RemoveInvisibleContent();
+                            if( rMergeDescriptor.bCreateSingleFile )
+                                pWorkDoc->RemoveInvisibleContent();
 
                             // launch MailMergeEvent if required
                             const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
@@ -1344,9 +1360,12 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                                     }
                                 }
                             }
-                            pWorkDoc->SetNewDBMgr( pOldDBMgr );
-
-                        xWorkDocSh->DoClose();
+                        if( rMergeDescriptor.bCreateSingleFile )
+                        {
+                            pWorkDoc->SetNewDBMgr( pOldDBManager );
+                            xWorkDocSh->DoClose();
+                            xWorkDocSh = NULL;
+                        }
                     }
                 }
                 nDocNo++;
@@ -1363,6 +1382,13 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                 }
             } while( !bCancel &&
                 (bSynchronizedDoc && (nStartRow != nEndRow)? ExistsNextRecord() : ToNextMergeRecord()));
+
+            if( !rMergeDescriptor.bCreateSingleFile )
+            {
+                pWorkDoc->SetNewDBMgr( pOldDBManager );
+                xWorkDocSh->DoClose();
+            }
+
             if (rMergeDescriptor.bCreateSingleFile)
             {
                 // sw::DocumentLayoutManager::CopyLayoutFmt() did not generate
commit 7f88e26321d112e1a3ed43e2ac4cba8d6b374a09
Author: Luboš Luňák <l.lunak at collabora.com>
Date:   Sun Feb 15 17:07:31 2015 +0100

    do not create temp file(s) during MM if not needed
    
    Change-Id: I1360e215bff42dd866ab1d94a18a8f2f9ddc7c66

diff --git a/sw/source/ui/dbui/dbmgr.cxx b/sw/source/ui/dbui/dbmgr.cxx
index fe9e339..79ebd0d 100644
--- a/sw/source/ui/dbui/dbmgr.cxx
+++ b/sw/source/ui/dbui/dbmgr.cxx
@@ -981,6 +981,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
 
             SwView* pTargetView = 0;
             std::auto_ptr< utl::TempFile > aTempFile;
+            bool createTempFile = ( rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL || rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE );
             String sModifiedStartingPageDesc;
             String sStartingPageDesc;
             sal_uInt16 nStartingPageNo = 0;
@@ -1095,7 +1096,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                     }
 
                     // create a new temporary file name - only done once in case of bCreateSingleFile
-                    if( 1 == nDocNo || !rMergeDescriptor.bCreateSingleFile )
+                    if( createTempFile && ( 1 == nDocNo || !rMergeDescriptor.bCreateSingleFile ))
                     {
                         INetURLObject aEntry(sPath);
                         String sLeading;
@@ -1111,23 +1112,25 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                                 new utl::TempFile(sLeading,&sExt,&sPath ));
                         if( rMergeDescriptor.bSubjectIsFilename )
                             aTempFile->EnableKillingFile();
+                        if( !aTempFile->IsValid() )
+                        {
+                            ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
+                            bNoError = false;
+                            bCancel = true;
+                        }
                     }
 
-                    if( !aTempFile->IsValid() )
-                    {
-                        ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
-                        bNoError = sal_False;
-                        bCancel = sal_True;
-                    }
-                    else
+                    if( !bCancel )
                     {
-                        INetURLObject aTempFileURL(aTempFile->GetURL());
+                        boost::scoped_ptr< INetURLObject > aTempFileURL;
+                        if( createTempFile )
+                            aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
                         if (!IsMergeSilent()) {
                             if( bMergeShell )
                                 ((CreateMonitor*) pProgressDlg)->SetCurrentPosition( nDocNo );
                             else {
                                 PrintMonitor *pPrintMonDlg = (PrintMonitor*) pProgressDlg;
-                                pPrintMonDlg->aPrinter.SetText( aTempFileURL.GetBase() );
+                                pPrintMonDlg->aPrinter.SetText( createTempFile ? aTempFileURL->GetBase() : OUString( pSourceDocSh->GetTitle( 22 )));
                                 OUString sStat(SW_RES(STR_STATSTR_LETTER));   // Brief
                                 sStat += " ";
                                 sStat += OUString::number( nDocNo );
@@ -1236,7 +1239,8 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
                             }
                             else
                             {
-                                String sFileURL =  aTempFileURL.GetMainURL( INetURLObject::NO_DECODE );
+                                assert( createTempFile );
+                                String sFileURL =  aTempFileURL->GetMainURL( INetURLObject::NO_DECODE );
                                 SfxMedium* pDstMed = new SfxMedium(
                                     sFileURL,
                                     STREAM_STD_READWRITE );
@@ -1391,7 +1395,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell,
             {
                 if( rMergeDescriptor.nMergeType != DBMGR_MERGE_PRINTER )
                 {
-                    OSL_ENSURE( aTempFile.get(), "Temporary file not available" );
+                    assert( aTempFile.get());
                     INetURLObject aTempFileURL( rMergeDescriptor.bSubjectIsFilename ? sSubject : aTempFile->GetURL());
                     SfxMedium* pDstMed = new SfxMedium(
                         aTempFileURL.GetMainURL( INetURLObject::NO_DECODE ),


More information about the Libreoffice-commits mailing list