tdf#117480 fix crash in mailmerge

Catch exceptions and hide progress dialog.

Change-Id: Ie63c8d7e001c90f40cf7504fd8248a6742e9d244
Reviewed-on: https://gerrit.libreoffice.org/65815
Reviewed-by: Szymon Kłos <szymon.klos@collabora.com>
Tested-by: Szymon Kłos <szymon.klos@collabora.com>
diff --git a/sw/source/uibase/dbui/dbmgr.cxx b/sw/source/uibase/dbui/dbmgr.cxx
index 46f16f0..7162c70 100644
--- a/sw/source/uibase/dbui/dbmgr.cxx
+++ b/sw/source/uibase/dbui/dbmgr.cxx
@@ -1269,453 +1269,463 @@
    vcl::Window *pSourceWindow = nullptr;
    std::shared_ptr<weld::GenericDialogController> xProgressDlg;

    if( !bIsMergeSilent )
    try
    {
        // construct the process dialog
        pSourceWindow = &pSourceShell->GetView().GetEditWin();
        if (!bMT_PRINTER)
            xProgressDlg.reset(new CreateMonitor(pSourceWindow->GetFrameWeld()));
        else
        if( !bIsMergeSilent )
        {
            xProgressDlg.reset(new PrintMonitor(pSourceWindow->GetFrameWeld()));
            static_cast<PrintMonitor*>(xProgressDlg.get())->set_title(
                pSourceDocSh->GetTitle(22));
        }
        weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){
            if (nResult == RET_CANCEL)
                MergeCancel();
            xProgressDlg.reset();
        });

        Application::Reschedule( true );
    }

    if( bCreateSingleFile && !pTargetView )
    {
        // create a target docshell to put the merged document into
        xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET,
            *pSourceShell, bMT_SHELL ? pSourceWindow : nullptr,
            nullptr, &pTargetView, &pTargetShell, &pTargetDoc );

        // import current print settings
        const SwPrintData &rPrintData = pSourceShell->getIDocumentDeviceAccess().getPrintData();
        pTargetDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);

        if (nMaxDumpDocs)
            lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
    }
    else if( pTargetView )
    {
        pTargetShell = pTargetView->GetWrtShellPtr();
        pTargetDoc = pTargetShell->GetDoc();
        xTargetDocShell = pTargetView->GetDocShell();
    }

    if( bCreateSingleFile )
    {
        // determine the page style and number used at the start of the source document
        pSourceShell->SttEndDoc(true);
        nStartingPageNo = pSourceShell->GetVirtPageNum();
    }

    // Progress, to prohibit KeyInputs
    SfxProgress aProgress(pSourceDocSh, OUString(), 1);

    // lock all dispatchers
    SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
    while (pViewFrame)
    {
        pViewFrame->GetDispatcher()->Lock(true);
        pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
    }

    sal_Int32 nDocNo = 1;

    // For single file mode, the number of pages in the target document so far, which is used
    // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
    // from the target doc would require repeated layouts of the doc, which is expensive, but
    // it can be manually computed from the source documents (for which we do layouts, so the page
    // count is known, and there is a blank page between each of them in the target document).
    int targetDocPageCount = 0;

    if( !bIsMergeSilent && !bMT_PRINTER )
    {
        sal_Int32 nRecordCount = 1;
        lcl_getCountFromResultSet( nRecordCount, pImpl->pMergeData );

        // Synchronized docs don't auto-advance the record set, but there is a
        // "security" check, which will always advance the record set, if there
        // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
        sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
                ->getIDocumentFieldsAccess().GetRecordsPerDocument();
        if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
            --nRecordPerDoc;
        assert( nRecordPerDoc > 0 );

        sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc;
        if ( 0 != nRecordCount % nRecordPerDoc )
            nMaxDocs += 1;
        static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs);
    }

    long nStartRow, nEndRow;
    bool bFreezedLayouts = false;
    // to collect temporary email files
    std::vector< OUString> aFilesToRemove;

    // The SfxObjectShell will be closed explicitly later but
    // it is more safe to use SfxObjectShellLock here
    SfxObjectShellLock xWorkDocSh;
    SwView*            pWorkView             = nullptr;
    SwDoc*             pWorkDoc              = nullptr;
    SwDBManager*       pWorkDocOrigDBManager = nullptr;
    SwWrtShell*        pWorkShell            = nullptr;
    bool               bWorkDocInitialized   = false;

    do
    {
        nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;

        OUString sColumnData;

        // Read the indicated data column, which should contain a valid mail
        // address or an optional file name
        if( bMT_EMAIL || bColumnName )
        {
            sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
        }

        // create a new temporary file name - only done once in case of bCreateSingleFile
        if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
        {
            OUString sPrefix = sDescriptorPrefix;
            OUString sLeading;

            //#i97667# if the name is from a database field then it will be used _as is_
            if( bColumnName && !bMT_EMAIL )
            {
                if (!sColumnData.isEmpty())
                    sLeading = sColumnData;
                else
                    sLeading = "_";
            }
            // construct the process dialog
            pSourceWindow = &pSourceShell->GetView().GetEditWin();
            if (!bMT_PRINTER)
                xProgressDlg.reset(new CreateMonitor(pSourceWindow->GetFrameWeld()));
            else
            {
                INetURLObject aEntry( sPrefix );
                sLeading = aEntry.GetBase();
                aEntry.removeSegment();
                sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
                xProgressDlg.reset(new PrintMonitor(pSourceWindow->GetFrameWeld()));
                static_cast<PrintMonitor*>(xProgressDlg.get())->set_title(
                    pSourceDocSh->GetTitle(22));
            }
            weld::DialogController::runAsync(xProgressDlg, [this, &xProgressDlg](sal_Int32 nResult){
                if (nResult == RET_CANCEL)
                    MergeCancel();
                xProgressDlg.reset();
            });

            OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
            aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPrefix, true) );
            if( !aTempFile->IsValid() )
            {
                ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
                m_aMergeStatus = MergeStatus::Error;
            }
            Application::Reschedule( true );
        }

        if( IsMergeOk() )
        if( bCreateSingleFile && !pTargetView )
        {
            std::unique_ptr< INetURLObject > aTempFileURL;
            if( bNeedsTempFiles )
                aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
            if( !bIsMergeSilent ) {
                if( !bMT_PRINTER )
                    static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo);
                else {
                    PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get());
                    pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles
                        ? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2));
                    OUString sStat( SwResId(STR_STATSTR_LETTER) );
                    sStat += " " + OUString::number( nDocNo );
                    pPrintMonDlg->m_xPrintInfo->set_label(sStat);
            // create a target docshell to put the merged document into
            xTargetDocShell = lcl_CreateWorkingDocument( WorkingDocType::TARGET,
                *pSourceShell, bMT_SHELL ? pSourceWindow : nullptr,
                nullptr, &pTargetView, &pTargetShell, &pTargetDoc );

            // import current print settings
            const SwPrintData &rPrintData = pSourceShell->getIDocumentDeviceAccess().getPrintData();
            pTargetDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);

            if (nMaxDumpDocs)
                lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
        }
        else if( pTargetView )
        {
            pTargetShell = pTargetView->GetWrtShellPtr();
            pTargetDoc = pTargetShell->GetDoc();
            xTargetDocShell = pTargetView->GetDocShell();
        }

        if( bCreateSingleFile )
        {
            // determine the page style and number used at the start of the source document
            pSourceShell->SttEndDoc(true);
            nStartingPageNo = pSourceShell->GetVirtPageNum();
        }

        // Progress, to prohibit KeyInputs
        SfxProgress aProgress(pSourceDocSh, OUString(), 1);

        // lock all dispatchers
        SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
        while (pViewFrame)
        {
            pViewFrame->GetDispatcher()->Lock(true);
            pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
        }

        sal_Int32 nDocNo = 1;

        // For single file mode, the number of pages in the target document so far, which is used
        // by AppendDoc() to adjust position of page-bound objects. Getting this information directly
        // from the target doc would require repeated layouts of the doc, which is expensive, but
        // it can be manually computed from the source documents (for which we do layouts, so the page
        // count is known, and there is a blank page between each of them in the target document).
        int targetDocPageCount = 0;

        if( !bIsMergeSilent && !bMT_PRINTER )
        {
            sal_Int32 nRecordCount = 1;
            lcl_getCountFromResultSet( nRecordCount, pImpl->pMergeData );

            // Synchronized docs don't auto-advance the record set, but there is a
            // "security" check, which will always advance the record set, if there
            // is no "next record" field in a synchronized doc => nRecordPerDoc > 0
            sal_Int32 nRecordPerDoc = pSourceShell->GetDoc()
                    ->getIDocumentFieldsAccess().GetRecordsPerDocument();
            if ( bSynchronizedDoc && (nRecordPerDoc > 1) )
                --nRecordPerDoc;
            assert( nRecordPerDoc > 0 );

            sal_Int32 nMaxDocs = nRecordCount / nRecordPerDoc;
            if ( 0 != nRecordCount % nRecordPerDoc )
                nMaxDocs += 1;
            static_cast<CreateMonitor*>(xProgressDlg.get())->SetTotalCount(nMaxDocs);
        }

        long nStartRow, nEndRow;
        bool bFreezedLayouts = false;
        // to collect temporary email files
        std::vector< OUString> aFilesToRemove;

        // The SfxObjectShell will be closed explicitly later but
        // it is more safe to use SfxObjectShellLock here
        SfxObjectShellLock xWorkDocSh;
        SwView*            pWorkView             = nullptr;
        SwDoc*             pWorkDoc              = nullptr;
        SwDBManager*       pWorkDocOrigDBManager = nullptr;
        SwWrtShell*        pWorkShell            = nullptr;
        bool               bWorkDocInitialized   = false;

        do
        {
            nStartRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;

            OUString sColumnData;

            // Read the indicated data column, which should contain a valid mail
            // address or an optional file name
            if( bMT_EMAIL || bColumnName )
            {
                sColumnData = GetDBField( xColumnProp, aColumnDBFormat );
            }

            // create a new temporary file name - only done once in case of bCreateSingleFile
            if( bNeedsTempFiles && ( !bWorkDocInitialized || !bCreateSingleFile ))
            {
                OUString sPrefix = sDescriptorPrefix;
                OUString sLeading;

                //#i97667# if the name is from a database field then it will be used _as is_
                if( bColumnName && !bMT_EMAIL )
                {
                    if (!sColumnData.isEmpty())
                        sLeading = sColumnData;
                    else
                        sLeading = "_";
                }
                //TODO xProgressDlg->queue_draw();
                else
                {
                    INetURLObject aEntry( sPrefix );
                    sLeading = aEntry.GetBase();
                    aEntry.removeSegment();
                    sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
                }

                OUString sExt(comphelper::string::stripStart(pStoreToFilter->GetDefaultExtension(), '*'));
                aTempFile.reset( new utl::TempFile(sLeading, sColumnData.isEmpty(), &sExt, &sPrefix, true) );
                if( !aTempFile->IsValid() )
                {
                    ErrorHandler::HandleError( ERRCODE_IO_NOTSUPPORTED );
                    m_aMergeStatus = MergeStatus::Error;
                }
            }

            if( IsMergeOk() )
            {
                std::unique_ptr< INetURLObject > aTempFileURL;
                if( bNeedsTempFiles )
                    aTempFileURL.reset( new INetURLObject(aTempFile->GetURL()));
                if( !bIsMergeSilent ) {
                    if( !bMT_PRINTER )
                        static_cast<CreateMonitor*>(xProgressDlg.get())->SetCurrentPosition(nDocNo);
                    else {
                        PrintMonitor *pPrintMonDlg = static_cast<PrintMonitor*>(xProgressDlg.get());
                        pPrintMonDlg->m_xPrinter->set_label(bNeedsTempFiles
                            ? aTempFileURL->GetBase() : pSourceDocSh->GetTitle( 2));
                        OUString sStat( SwResId(STR_STATSTR_LETTER) );
                        sStat += " " + OUString::number( nDocNo );
                        pPrintMonDlg->m_xPrintInfo->set_label(sStat);
                    }
                    //TODO xProgressDlg->queue_draw();
                }

                Application::Reschedule( 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. Currently PDF also has to be treated special.
                if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport )
                {
                    assert( !xWorkDocSh.Is());
                    pWorkDocOrigDBManager = this;
                    xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
                        *pSourceShell, nullptr, &pWorkDocOrigDBManager,
                        &pWorkView, &pWorkShell, &pWorkDoc );
                    if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                        lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );

                    // #i69458# lock fields to prevent access to the result set while calculating layout
                    // tdf#92324: and do not unlock: keep document locked during printing to avoid
                    // ExpFields update during printing, generation of preview, etc.
                    pWorkShell->LockExpFields();
                    pWorkShell->CalcLayout();
                    // tdf#121168: Now force correct page descriptions applied to page frames. Without
                    // this, e.g., page frames starting with sections could have page descriptions set
                    // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
                    pWorkShell->GetViewOptions()->SetIdle(true);
                    for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts())
                    {
                        aLayout->FreezeLayout(false);
                        aLayout->AllCheckPageDescs();
                    }
                }

                lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh);

                // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
                // database cursor movement on any other fields update, for example during
                // print preview and other operations
                if ( pWorkShell->IsExpFieldsLocked() )
                    pWorkShell->UnlockExpFields();
                pWorkShell->SwViewShell::UpdateFields();
                pWorkShell->LockExpFields();

                lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh);

                // also emit MailMergeEvent on XInterface if possible
                const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
                if(pEvtSrc)
                {
                    uno::Reference< uno::XInterface > xRef(
                        static_cast<text::XMailMergeBroadcaster*>(const_cast<SwXMailMerge*>(pEvtSrc)) );
                    text::MailMergeEvent aEvt( xRef, xWorkDocSh->GetModel() );
                    pEvtSrc->LaunchMailMergeEvent( aEvt );
                }

                // working copy is merged - prepare final steps depending on merge options

                if( bCreateSingleFile )
                {
                    assert( pTargetShell && "no target shell available!" );

                    // prepare working copy and target to append

                    pWorkDoc->RemoveInvisibleContent();
                    pWorkShell->ConvertFieldsToText();
                    pWorkShell->SetNumberingRestart();
                    if( bSynchronizedDoc )
                    {
                        lcl_RemoveSectionLinks( *pWorkShell );
                    }

                    if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                        lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );

                    // append the working document to the target document
                    if( targetDocPageCount % 2 == 1 )
                        ++targetDocPageCount; // Docs always start on odd pages (so offset must be even).
                    SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc,
                        nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo);
                    // ensure layout is up to date in order to get correct page count
                    pWorkShell->CalcLayout();
                    targetDocPageCount += pWorkShell->GetPageCnt();

                    if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                        lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );

                    if (bMT_SHELL)
                    {
                        SwDocMergeInfo aMergeInfo;
                        // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
                        aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark(
                            appendedDocStart, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
                            ::sw::mark::InsertMode::New);
                        aMergeInfo.nDBRow = nStartRow;
                        rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
                    }
                }
                else
                {
                    assert( bNeedsTempFiles );
                    assert( pWorkShell->IsExpFieldsLocked() );

                    // fields are locked, so it's fine to
                    // restore the old / empty DB manager for save
                    pWorkDoc->SetDBManager( pWorkDocOrigDBManager );

                    // save merged document
                    OUString sFileURL;
                    if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions,
                                    &rMergeDescriptor.aSaveToFilterData, bIsPDFexport,
                                    xWorkDocSh, *pWorkShell, &sFileURL ) )
                    {
                        m_aMergeStatus = MergeStatus::Error;
                    }

                    // back to the MM DB manager
                    pWorkDoc->SetDBManager( this );

                    if( bMT_EMAIL && !IsMergeError() )
                    {
                        // schedule file for later removal
                        aFilesToRemove.push_back( sFileURL );

                        if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) )
                        {
                            OSL_FAIL("invalid e-Mail address in database column");
                        }
                        else
                        {
                            uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc(
                                rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
                                sMailEncoding, pStoreToFilter->GetMimeType() );
                            if( xMessage.is() )
                            {
                                osl::MutexGuard aGuard( pImpl->m_aAllEmailSendMutex );
                                pImpl->m_xLastMessage.set( xMessage );
                                xMailDispatcher->enqueueMailMessage( xMessage );
                                if( !xMailDispatcher->isStarted() )
                                    xMailDispatcher->start();
                            }
                        }
                    }
                }
                if( bCreateSingleFile || bIsPDFexport )
                {
                    pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
                    xWorkDocSh->DoClose();
                    xWorkDocSh = nullptr;
                }
            }

            bWorkDocInitialized = true;
            nDocNo++;
            nEndRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;

            // Freeze the layouts of the target document after the first inserted
            // sub-document, to get the correct PageDesc.
            if(!bFreezedLayouts && bCreateSingleFile)
            {
                for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
                    aLayout->FreezeLayout(true);
                bFreezedLayouts = true;
            }
        } while( IsMergeOk() &&
            ((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));

        if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
        {
            // Unlock document fields after merge complete
            pWorkView->GetWrtShell().UnlockExpFields();
        }

        if( !bCreateSingleFile )
        {
            if( bMT_PRINTER )
                Printer::FinishPrintJob( pWorkView->GetPrinterController());
            if( !bIsPDFexport )
            {
                pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
                xWorkDocSh->DoClose();
            }
        }
        else if( IsMergeOk() ) // && bCreateSingleFile
        {
            Application::Reschedule( true );

            // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
            // unique fly names, do it here once.
            pTargetDoc->SetInMailMerge(false);
            pTargetDoc->SetAllUniqueFlyNames();

            // Unfreeze target document layouts and correct all PageDescs.
            SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
            pTargetShell->CalcLayout();
            SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
            pTargetShell->GetViewOptions()->SetIdle( true );
            pTargetDoc->GetIDocumentUndoRedo().DoUndo( true );
            for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
            {
                aLayout->FreezeLayout(false);
                aLayout->AllCheckPageDescs();
            }

            Application::Reschedule( 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. Currently PDF also has to be treated special.
            if( !bWorkDocInitialized || bCreateSingleFile || bIsPDFexport )
            if( IsMergeOk() && bMT_FILE )
            {
                assert( !xWorkDocSh.Is());
                pWorkDocOrigDBManager = this;
                xWorkDocSh = lcl_CreateWorkingDocument( WorkingDocType::COPY,
                    *pSourceShell, nullptr, &pWorkDocOrigDBManager,
                    &pWorkView, &pWorkShell, &pWorkDoc );
                if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                    lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );

                // #i69458# lock fields to prevent access to the result set while calculating layout
                // tdf#92324: and do not unlock: keep document locked during printing to avoid
                // ExpFields update during printing, generation of preview, etc.
                pWorkShell->LockExpFields();
                pWorkShell->CalcLayout();
                // tdf#121168: Now force correct page descriptions applied to page frames. Without
                // this, e.g., page frames starting with sections could have page descriptions set
                // wrong. This would lead to wrong page styles applied in SwDoc::AppendDoc below.
                pWorkShell->GetViewOptions()->SetIdle(true);
                for (auto aLayout : pWorkShell->GetDoc()->GetAllLayouts())
                {
                    aLayout->FreezeLayout(false);
                    aLayout->AllCheckPageDescs();
                }
            }

            lcl_emitEvent(SfxEventHintId::SwEventFieldMerge, STR_SW_EVENT_FIELD_MERGE, xWorkDocSh);

            // tdf#92324: Allow ExpFields update only by explicit instruction to avoid
            // database cursor movement on any other fields update, for example during
            // print preview and other operations
            if ( pWorkShell->IsExpFieldsLocked() )
                pWorkShell->UnlockExpFields();
            pWorkShell->SwViewShell::UpdateFields();
            pWorkShell->LockExpFields();

            lcl_emitEvent(SfxEventHintId::SwEventFieldMergeFinished, STR_SW_EVENT_FIELD_MERGE_FINISHED, xWorkDocSh);

            // also emit MailMergeEvent on XInterface if possible
            const SwXMailMerge *pEvtSrc = GetMailMergeEvtSrc();
            if(pEvtSrc)
            {
                uno::Reference< uno::XInterface > xRef(
                    static_cast<text::XMailMergeBroadcaster*>(const_cast<SwXMailMerge*>(pEvtSrc)) );
                text::MailMergeEvent aEvt( xRef, xWorkDocSh->GetModel() );
                pEvtSrc->LaunchMailMergeEvent( aEvt );
            }

            // working copy is merged - prepare final steps depending on merge options

            if( bCreateSingleFile )
            {
                assert( pTargetShell && "no target shell available!" );

                // prepare working copy and target to append

                pWorkDoc->RemoveInvisibleContent();
                pWorkShell->ConvertFieldsToText();
                pWorkShell->SetNumberingRestart();
                if( bSynchronizedDoc )
                {
                    lcl_RemoveSectionLinks( *pWorkShell );
                }

                if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                    lcl_SaveDebugDoc( xWorkDocSh, "WorkDoc", nDocNo );

                // append the working document to the target document
                if( targetDocPageCount % 2 == 1 )
                    ++targetDocPageCount; // Docs always start on odd pages (so offset must be even).
                SwNodeIndex appendedDocStart = pTargetDoc->AppendDoc( *pWorkDoc,
                    nStartingPageNo, !bWorkDocInitialized, targetDocPageCount, nDocNo);
                // ensure layout is up to date in order to get correct page count
                pWorkShell->CalcLayout();
                targetDocPageCount += pWorkShell->GetPageCnt();

                if ( (nMaxDumpDocs < 0) || (nDocNo <= nMaxDumpDocs) )
                    lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );

                if (bMT_SHELL)
                {
                    SwDocMergeInfo aMergeInfo;
                    // Name of the mark is actually irrelevant, UNO bookmarks have internals names.
                    aMergeInfo.startPageInTarget = pTargetDoc->getIDocumentMarkAccess()->makeMark(
                        appendedDocStart, "", IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
                        ::sw::mark::InsertMode::New);
                    aMergeInfo.nDBRow = nStartRow;
                    rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
                }
            }
            else
            {
                assert( bNeedsTempFiles );
                assert( pWorkShell->IsExpFieldsLocked() );

                // fields are locked, so it's fine to
                // restore the old / empty DB manager for save
                pWorkDoc->SetDBManager( pWorkDocOrigDBManager );

                // save merged document
                OUString sFileURL;
                if( !lcl_SaveDoc( aTempFileURL.get(), pStoreToFilter, pStoreToFilterOptions,
                                  &rMergeDescriptor.aSaveToFilterData, bIsPDFexport,
                                  xWorkDocSh, *pWorkShell, &sFileURL ) )
                assert( aTempFile.get() );
                INetURLObject aTempFileURL;
                if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename)
                    aTempFileURL.SetURL( aTempFile->GetURL() );
                else
                {
                    aTempFileURL.SetURL(sDescriptorPrefix);
                    // remove the unneeded temporary file
                    aTempFile->EnableKillingFile();
                }
                if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter,
                        pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData,
                        bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) )
                {
                    m_aMergeStatus = MergeStatus::Error;
                }

                // back to the MM DB manager
                pWorkDoc->SetDBManager( this );

                if( bMT_EMAIL && !IsMergeError() )
                {
                    // schedule file for later removal
                    aFilesToRemove.push_back( sFileURL );

                    if( !SwMailMergeHelper::CheckMailAddress( sColumnData ) )
                    {
                        OSL_FAIL("invalid e-Mail address in database column");
                    }
                    else
                    {
                        uno::Reference< mail::XMailMessage > xMessage = lcl_CreateMailFromDoc(
                            rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
                            sMailEncoding, pStoreToFilter->GetMimeType() );
                        if( xMessage.is() )
                        {
                            osl::MutexGuard aGuard( pImpl->m_aAllEmailSendMutex );
                            pImpl->m_xLastMessage.set( xMessage );
                            xMailDispatcher->enqueueMailMessage( xMessage );
                            if( !xMailDispatcher->isStarted() )
                                xMailDispatcher->start();
                        }
                    }
                }
            }
            if( bCreateSingleFile || bIsPDFexport )
            else if( IsMergeOk() && bMT_PRINTER )
            {
                pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
                xWorkDocSh->DoClose();
                xWorkDocSh = nullptr;
                // print the target document
                uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
                lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
                pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
            }
        }

        bWorkDocInitialized = true;
        nDocNo++;
        nEndRow = pImpl->pMergeData ? pImpl->pMergeData->xResultSet->getRow() : 0;

        // Freeze the layouts of the target document after the first inserted
        // sub-document, to get the correct PageDesc.
        if(!bFreezedLayouts && bCreateSingleFile)
        {
            for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
                aLayout->FreezeLayout(true);
            bFreezedLayouts = true;
        }
    } while( IsMergeOk() &&
        ((bSynchronizedDoc && (nStartRow != nEndRow)) ? IsValidMergeRecord() : ToNextMergeRecord()));

    if ( xWorkDocSh.Is() && pWorkView->GetWrtShell().IsExpFieldsLocked() )
    {
        // Unlock document fields after merge complete
        pWorkView->GetWrtShell().UnlockExpFields();
    }

    if( !bCreateSingleFile )
    {
        if( bMT_PRINTER )
            Printer::FinishPrintJob( pWorkView->GetPrinterController());
        if( !bIsPDFexport )
        {
            pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
            xWorkDocSh->DoClose();
        }
    }
    else if( IsMergeOk() ) // && bCreateSingleFile
    {
        Application::Reschedule( true );

        // sw::DocumentLayoutManager::CopyLayoutFormat() did not generate
        // unique fly names, do it here once.
        pTargetDoc->SetInMailMerge(false);
        pTargetDoc->SetAllUniqueFlyNames();

        // Unfreeze target document layouts and correct all PageDescs.
        SAL_INFO( "sw.pageframe", "(MergeMailFiles pTargetShell->CalcLayout in" );
        pTargetShell->CalcLayout();
        SAL_INFO( "sw.pageframe", "MergeMailFiles pTargetShell->CalcLayout out)" );
        pTargetShell->GetViewOptions()->SetIdle( true );
        pTargetDoc->GetIDocumentUndoRedo().DoUndo( true );
        for ( auto aLayout : pTargetShell->GetDoc()->GetAllLayouts() )
        {
            aLayout->FreezeLayout(false);
            aLayout->AllCheckPageDescs();
        }
        // we also show canceled documents, as long as there was no error
        if( !IsMergeError() && bMT_SHELL )
            // leave docshell available for caller (e.g. MM wizard)
            rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
        else if( xTargetDocShell.is() )
            xTargetDocShell->DoClose();

        Application::Reschedule( true );

        if( IsMergeOk() && bMT_FILE )
        if (xProgressDlg)
        {
            // save merged document
            assert( aTempFile.get() );
            INetURLObject aTempFileURL;
            if (sDescriptorPrefix.isEmpty() || !rMergeDescriptor.bPrefixIsFilename)
                aTempFileURL.SetURL( aTempFile->GetURL() );
            else
            xProgressDlg->response(RET_OK);
        }

        // unlock all dispatchers
        pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
        while (pViewFrame)
        {
            pViewFrame->GetDispatcher()->Lock(false);
            pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
        }

        SW_MOD()->SetView(&pSourceShell->GetView());

        if( xMailDispatcher.is() )
        {
            if( IsMergeOk() )
            {
                aTempFileURL.SetURL(sDescriptorPrefix);
                // remove the unneeded temporary file
                aTempFile->EnableKillingFile();
                // TODO: Instead of polling via an AutoTimer, post an Idle event,
                // if the main loop has been made thread-safe.
                AutoTimer aEmailDispatcherPollTimer;
                aEmailDispatcherPollTimer.SetDebugName(
                    "sw::SwDBManager aEmailDispatcherPollTimer" );
                aEmailDispatcherPollTimer.SetTimeout( 500 );
                aEmailDispatcherPollTimer.Start();
                while( IsMergeOk() && pImpl->m_xLastMessage.is() )
                    Application::Yield();
                aEmailDispatcherPollTimer.Stop();
            }
            if( !lcl_SaveDoc( &aTempFileURL, pStoreToFilter,
                    pStoreToFilterOptions, &rMergeDescriptor.aSaveToFilterData,
                    bIsPDFexport, xTargetDocShell.get(), *pTargetShell ) )
            {
                m_aMergeStatus = MergeStatus::Error;
            }
            xMailDispatcher->stop();
            xMailDispatcher->shutdown();
        }
        else if( IsMergeOk() && bMT_PRINTER )

        // remove the temporary files
        // has to be done after xMailDispatcher is finished, as mails may be
        // delivered as message attachments!
        for( const OUString &sFileURL : aFilesToRemove )
            SWUnoHelper::UCB_DeleteFile( sFileURL );
    }
    catch (const uno::Exception&)
    {
        if (xProgressDlg)
        {
            // print the target document
            uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
            lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
            pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
            xProgressDlg->response(RET_CANCEL);
        }
    }

    // we also show canceled documents, as long as there was no error
    if( !IsMergeError() && bMT_SHELL )
        // leave docshell available for caller (e.g. MM wizard)
        rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView );
    else if( xTargetDocShell.is() )
        xTargetDocShell->DoClose();

    Application::Reschedule( true );

    if (xProgressDlg)
    {
        xProgressDlg->response(RET_OK);
    }

    // unlock all dispatchers
    pViewFrame = SfxViewFrame::GetFirst(pSourceDocSh);
    while (pViewFrame)
    {
        pViewFrame->GetDispatcher()->Lock(false);
        pViewFrame = SfxViewFrame::GetNext(*pViewFrame, pSourceDocSh);
    }

    SW_MOD()->SetView(&pSourceShell->GetView());

    if( xMailDispatcher.is() )
    {
        if( IsMergeOk() )
        {
            // TODO: Instead of polling via an AutoTimer, post an Idle event,
            // if the main loop has been made thread-safe.
            AutoTimer aEmailDispatcherPollTimer;
            aEmailDispatcherPollTimer.SetDebugName(
                "sw::SwDBManager aEmailDispatcherPollTimer" );
            aEmailDispatcherPollTimer.SetTimeout( 500 );
            aEmailDispatcherPollTimer.Start();
            while( IsMergeOk() && pImpl->m_xLastMessage.is() )
                Application::Yield();
            aEmailDispatcherPollTimer.Stop();
        }
        xMailDispatcher->stop();
        xMailDispatcher->shutdown();
    }

    // remove the temporary files
    // has to be done after xMailDispatcher is finished, as mails may be
    // delivered as message attachments!
    for( const OUString &sFileURL : aFilesToRemove )
        SWUnoHelper::UCB_DeleteFile( sFileURL );

    return !IsMergeError();
}