Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sw/source/uibase/dbui/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 122 kB image not shown  

SSL dbmgr.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <cassert>

#include <unotxdoc.hxx>
#include <sfx2/app.hxx>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/sdb/XDocumentDataSource.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/XEventListener.hpp>
#include <com/sun/star/uri/UriReferenceFactory.hpp>
#include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/sdb/DatabaseContext.hpp>
#include <com/sun/star/sdb/TextConnectionSettings.hpp>
#include <com/sun/star/sdb/XCompletedConnection.hpp>
#include <com/sun/star/sdb/XCompletedExecution.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/text/MailMergeEvent.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <vcl/errinf.hxx>
#include <vcl/print.hxx>
#include <vcl/scheduler.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/filedlghelper.hxx>
#include <sfx2/viewfrm.hxx>
#include <dbconfig.hxx>
#include <unotools/tempfile.hxx>
#include <unotools/pathoptions.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/stritem.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/progress.hxx>
#include <sfx2/dispatch.hxx>
#include <cmdid.h>
#include <swmodule.hxx>
#include <view.hxx>
#include <docsh.hxx>
#include <edtwin.hxx>
#include <wrtsh.hxx>
#include <fldbas.hxx>
#include <dbui.hxx>
#include <dbmgr.hxx>
#include <doc.hxx>
#include <IDocumentLinksAdministration.hxx>
#include <IDocumentFieldsAccess.hxx>
#include <IDocumentUndoRedo.hxx>
#include <swwait.hxx>
#include <swunohelper.hxx>
#include <strings.hrc>
#include <mmconfigitem.hxx>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
#include <com/sun/star/sdb/XQueriesSupplier.hpp>
#include <com/sun/star/sdb/XColumn.hpp>
#include <com/sun/star/sdbc/DataType.hpp>
#include <com/sun/star/sdbc/ResultSetType.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/mail/MailAttachment.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/property.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/scopeguard.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/string.hxx>
#include <comphelper/types.hxx>
#include <mailmergehelper.hxx>
#include <maildispatcher.hxx>
#include <svtools/htmlcfg.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <com/sun/star/util/XNumberFormatTypes.hpp>
#include <svl/numuno.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/dbconversion.hxx>
#include <unotools/charclass.hxx>
#include <comphelper/diagnose_ex.hxx>

#include <unomailmerge.hxx>
#include <sfx2/event.hxx>
#include <svx/dataaccessdescriptor.hxx>
#include <rtl/textenc.h>
#include <rtl/tencinfo.h>
#include <cppuhelper/implbase.hxx>
#include <ndindex.hxx>
#include <swevent.hxx>
#include <sal/log.hxx>
#include <swabstdlg.hxx>
#include <vector>
#include <section.hxx>
#include <rootfrm.hxx>
#include <calc.hxx>
#include <dbfld.hxx>
#include <IDocumentState.hxx>
#include <imaildsplistener.hxx>
#include <iodetect.hxx>
#include <IDocumentDeviceAccess.hxx>

#include <memory>
#include <mutex>
#include <comphelper/propertysequence.hxx>

using namespace ::com::sun::star;
using namespace sw;

namespace {

void lcl_emitEvent(SfxEventHintId nEventId, sal_Int32 nStrId, SfxObjectShell* pDocShell)
{
    SfxGetpApp()->NotifyEvent(SfxEventHint(nEventId,
                                           SwDocShell::GetEventName(nStrId),
                                           pDocShell));
}

// Construct vnd.sun.star.pkg:// URL
OUString ConstructVndSunStarPkgUrl(const OUString& rMainURL, std::u16string_view rStreamRelPath)
{
    const auto& xContext(comphelper::getProcessComponentContext());
    auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(rMainURL);
    assert(xUri.is());
    xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)
        ->createVndSunStarPkgUrlReference(xUri);
    assert(xUri.is());
    return xUri->getUriReference() + "/"
        + INetURLObject::encode(
            rStreamRelPath, INetURLObject::PART_FPATH,
            INetURLObject::EncodeMechanism::All);
}

}

std::vector<std::pair<SwDocShell*, OUString>> SwDBManager::s_aUncommittedRegistrations;

namespace {

enum class SwDBNextRecord { NEXT, FIRST };

}

static bool lcl_ToNextRecord( SwDSParam* pParam, const SwDBNextRecord action = SwDBNextRecord::NEXT );

namespace {

enum class WorkingDocType { SOURCE, TARGET, COPY };

}

static SfxObjectShell* lcl_CreateWorkingDocument(
    const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
    const vcl::Window *pSourceWindow,
    SwDBManager** const ppDBManager,
    SwView** const pView, SwWrtShell** const pWrtShell, rtl::Reference<SwDoc>* const pDoc );

static bool lcl_getCountFromResultSet( sal_Int32& rCount, const SwDSParam* pParam )
{
    rCount = pParam->aSelection.getLength();
    if ( rCount > 0 )
        return true;

    uno::Reference<beans::XPropertySet> xPrSet(pParam->xResultSet, uno::UNO_QUERY);
    if ( xPrSet.is() )
    {
        try
        {
            bool bFinal = false;
            uno::Any aFinal = xPrSet->getPropertyValue(u"IsRowCountFinal"_ustr);
            aFinal >>= bFinal;
            if(!bFinal)
            {
                pParam->xResultSet->last();
                pParam->xResultSet->first();
            }
            uno::Any aCount = xPrSet->getPropertyValue(u"RowCount"_ustr);
            if( aCount >>= rCount )
                return true;
        }
        catch(const uno::Exception&)
        {
        }
    }
    return false;
}

class SwDBManager::ConnectionDisposedListener_Impl
    : public cppu::WeakImplHelper< lang::XEventListener >
{
private:
    SwDBManager * m_pDBManager;

    virtual void SAL_CALL disposing( const lang::EventObject& Source ) override;

public:
    explicit ConnectionDisposedListener_Impl(SwDBManager& rMgr);

    void Dispose() { m_pDBManager = nullptr; }

};

namespace {

/// Listens to removed data sources, and if it's one that's embedded into this document, triggers embedding removal.
class SwDataSourceRemovedListener : public cppu::WeakImplHelper<sdb::XDatabaseRegistrationsListener>
{
    uno::Reference<sdb::XDatabaseContext> m_xDatabaseContext;
    SwDBManager* m_pDBManager;

public:
    explicit SwDataSourceRemovedListener(SwDBManager& rDBManager);
    virtual ~SwDataSourceRemovedListener() override;
    virtual void SAL_CALL registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent) override;
    virtual void SAL_CALL revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent&&nbsp;rEvent) override;
    virtual void SAL_CALL changedDatabaseLocation(const sdb::DatabaseRegistrationEvent&&nbsp;rEvent) override;
    virtual void SAL_CALL disposing(const lang::EventObject& rObject) override;
    void Dispose();
};

}

SwDataSourceRemovedListener::SwDataSourceRemovedListener(SwDBManager& rDBManager)
    : m_pDBManager(&rDBManager)
{
    const uno::Reference<uno::XComponentContext>& xComponentContext(comphelper::getProcessComponentContext());
    m_xDatabaseContext = sdb::DatabaseContext::create(xComponentContext);
    m_xDatabaseContext->addDatabaseRegistrationsListener(this);
}

SwDataSourceRemovedListener::~SwDataSourceRemovedListener()
{
    if (m_xDatabaseContext.is())
        m_xDatabaseContext->removeDatabaseRegistrationsListener(this);
}

void SAL_CALL SwDataSourceRemovedListener::registeredDatabaseLocation(const sdb::DatabaseRegistrationEvent& /*rEvent*/)
{
}

void SAL_CALL SwDataSourceRemovedListener::revokedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
{
    if (!m_pDBManager || m_pDBManager->getEmbeddedName().isEmpty())
        return;

    SwDoc* pDoc = m_pDBManager->getDoc();
    if (!pDoc)
        return;

    SwDocShell* pDocShell = pDoc->GetDocShell();
    if (!pDocShell)
        return;

    const OUString sTmpName = ConstructVndSunStarPkgUrl(
        pDocShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE),
        m_pDBManager->getEmbeddedName());

    if (sTmpName != rEvent.OldLocation)
        return;

    // The revoked database location is inside this document, then remove the
    // embedding, as otherwise it would be back on the next reload of the
    // document.
    pDocShell->GetStorage()->removeElement(m_pDBManager->getEmbeddedName());
    m_pDBManager->setEmbeddedName(OUString(), *pDocShell);
}

void SAL_CALL SwDataSourceRemovedListener::changedDatabaseLocation(const sdb::DatabaseRegistrationEvent& rEvent)
{
    if (rEvent.OldLocation != rEvent.NewLocation)
        revokedDatabaseLocation(rEvent);
}

void SwDataSourceRemovedListener::disposing(const lang::EventObject& /*rObject*/)
{
    m_xDatabaseContext.clear();
}

void SwDataSourceRemovedListener::Dispose()
{
    m_pDBManager = nullptr;
}

struct SwDBManager::SwDBManager_Impl
{
    std::unique_ptr<SwDSParam> pMergeData;
    VclPtr<AbstractMailMergeDlg>  pMergeDialog;
    rtl::Reference<SwDBManager::ConnectionDisposedListener_Impl> m_xDisposeListener;
    rtl::Reference<SwDataSourceRemovedListener> m_xDataSourceRemovedListener;
    std::mutex                    m_aAllEmailSendMutex;
    uno::Reference< mail::XMailMessage> m_xLastMessage;

    explicit SwDBManager_Impl(SwDBManager& rDBManager)
        : m_xDisposeListener(new ConnectionDisposedListener_Impl(rDBManager))
        {}

    ~SwDBManager_Impl()
    {
        m_xDisposeListener->Dispose();
        if (m_xDataSourceRemovedListener.is())
            m_xDataSourceRemovedListener->Dispose();
    }
};

static void lcl_InitNumberFormatter(SwDSParam& rParam, uno::Reference<sdbc::XDataSource> const & xSource)
{
    const uno::Reference<uno::XComponentContext>& xContext = ::comphelper::getProcessComponentContext();
    rParam.xFormatter = util::NumberFormatter::create(xContext);
    uno::Reference<beans::XPropertySet> xSourceProps(
        (xSource.is()
         ? xSource
         : SwDBManager::getDataSourceAsParent(
             rParam.xConnection, rParam.sDataSource)),
        uno::UNO_QUERY);
    if(!xSourceProps.is())
        return;

    uno::Any aFormats = xSourceProps->getPropertyValue(u"NumberFormatsSupplier"_ustr);
    if(!aFormats.hasValue())
        return;

    uno::Reference<util::XNumberFormatsSupplier> xSuppl;
    aFormats >>= xSuppl;
    if(xSuppl.is())
    {
        uno::Reference< beans::XPropertySet > xSettings = xSuppl->getNumberFormatSettings();
        uno::Any aNull = xSettings->getPropertyValue(u"NullDate"_ustr);
        aNull >>= rParam.aNullDate;
        if(rParam.xFormatter.is())
            rParam.xFormatter->attachNumberFormatsSupplier(xSuppl);
    }
}

static bool lcl_MoveAbsolute(SwDSParam* pParam, tools::Long nAbsPos)
{
    bool bRet = false;
    try
    {
        if(pParam->aSelection.hasElements())
        {
            if(pParam->aSelection.getLength() <= nAbsPos)
            {
                pParam->bEndOfDB = true;
                bRet = false;
            }
            else
            {
                pParam->nSelectionIndex = nAbsPos;
                sal_Int32 nPos = 0;
                pParam->aSelection.getConstArray()[ pParam->nSelectionIndex ] >>= nPos;
                pParam->bEndOfDB = !pParam->xResultSet->absolute( nPos );
                bRet = !pParam->bEndOfDB;
            }
        }
        else if(pParam->bScrollable)
        {
            bRet = pParam->xResultSet->absolute( nAbsPos );
        }
        else
        {
            OSL_FAIL("no absolute positioning available");
        }
    }
    catch(const uno::Exception&)
    {
    }
    return bRet;
}

static void lcl_GetColumnCnt(SwDSParam *pParam,
                             const uno::Reference< beans::XPropertySet > &rColumnProps,
                             LanguageType nLanguage, OUString &rResult, double* pNumber)
{
    SwDBFormatData aFormatData;
    if(!pParam->xFormatter.is())
    {
        uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(
                                    pParam->xConnection,pParam->sDataSource);
        lcl_InitNumberFormatter(*pParam, xSource );
    }
    aFormatData.aNullDate = pParam->aNullDate;
    aFormatData.xFormatter = pParam->xFormatter;

    aFormatData.aLocale = LanguageTag( nLanguage ).getLocale();

    rResult = SwDBManager::GetDBField( rColumnProps, aFormatData, pNumber);
}

static bool lcl_GetColumnCnt(SwDSParam* pParam, const OUString& rColumnName,
                             LanguageType nLanguage, OUString& rResult, double* pNumber)
{
    uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( pParam->xResultSet, uno::UNO_QUERY );
    uno::Reference<container::XNameAccess> xCols;
    try
    {
        xCols = xColsSupp->getColumns();
    }
    catch(const lang::DisposedException&)
    {
    }
    if(!xCols.is() || !xCols->hasByName(rColumnName))
        return false;
    uno::Any aCol = xCols->getByName(rColumnName);
    uno::Reference< beans::XPropertySet > xColumnProps;
    aCol >>= xColumnProps;
    lcl_GetColumnCnt( pParam, xColumnProps, nLanguage, rResult, pNumber );
    return true;
};

// import data
bool SwDBManager::Merge( const SwMergeDescriptor& rMergeDesc )
{
    assert( !m_bInMerge && !m_pImpl->pMergeData && "merge already activated!" );

    SfxObjectShellLock  xWorkObjSh;
    SwWrtShell         *pWorkShell            = nullptr;
    rtl::Reference<SwDoc> pWorkDoc;
    SwDBManager        *pWorkDocOrigDBManager = nullptr;

    switch( rMergeDesc.nMergeType )
    {
        case DBMGR_MERGE_PRINTER:
        case DBMGR_MERGE_EMAIL:
        case DBMGR_MERGE_FILE:
        case DBMGR_MERGE_SHELL:
        {
            SwDocShell *pSourceDocSh = rMergeDesc.rSh.GetView().GetDocShell();
            if( pSourceDocSh->IsModified() )
            {
                pWorkDocOrigDBManager = this;
                xWorkObjSh = lcl_CreateWorkingDocument(
                    WorkingDocType::SOURCE, rMergeDesc.rSh, nullptr,
                    &pWorkDocOrigDBManager, nullptr, &pWorkShell, &pWorkDoc );
            }
            [[fallthrough]];
        }

        default:
            if( !xWorkObjSh.Is() )
                pWorkShell = &rMergeDesc.rSh;
            break;
    }

    SwDBData aData;
    aData.nCommandType = sdb::CommandType::TABLE;
    uno::Reference<sdbc::XResultSet>  xResSet;
    uno::Sequence<uno::Any> aSelection;
    uno::Reference< sdbc::XConnection> xConnection;

    aData.sDataSource = rMergeDesc.rDescriptor.getDataSource();
    rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Command]      >>= aData.sCommand;
    rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::CommandType]  >>= aData.nCommandType;

    if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Cursor) )
        rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Cursor] >>= xResSet;
    if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Selection) )
        rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Selection] >>= aSelection;
    if ( rMergeDesc.rDescriptor.has(svx::DataAccessDescriptorProperty::Connection) )
        rMergeDesc.rDescriptor[svx::DataAccessDescriptorProperty::Connection] >>= xConnection;

    if((aData.sDataSource.isEmpty() || aData.sCommand.isEmpty()) && !xResSet.is())
    {
        return false;
    }

    m_pImpl->pMergeData.reset(new SwDSParam(aData, xResSet, aSelection));
    SwDSParam*  pTemp = FindDSData(aData, false);
    if(pTemp)
        *pTemp = *m_pImpl->pMergeData;
    else
    {
        // calls from the calculator may have added a connection with an invalid commandtype
        //"real" data base connections added here have to re-use the already available
        //DSData and set the correct CommandType
        aData.nCommandType = -1;
        pTemp = FindDSData(aData, false);
        if(pTemp)
            *pTemp = *m_pImpl->pMergeData;
        else
        {
            m_DataSourceParams.push_back(std::make_unique<SwDSParam>(*m_pImpl->pMergeData));
            try
            {
                uno::Reference<lang::XComponent> xComponent(m_DataSourceParams.back()->xConnection, uno::UNO_QUERY);
                if(xComponent.is())
                    xComponent->addEventListener(m_pImpl->m_xDisposeListener);
            }
            catch(const uno::Exception&)
            {
            }
        }
    }
    if(!m_pImpl->pMergeData->xConnection.is())
        m_pImpl->pMergeData->xConnection = xConnection;
    // add an XEventListener

    lcl_ToNextRecord(m_pImpl->pMergeData.get(), SwDBNextRecord::FIRST);

    uno::Reference<sdbc::XDataSource> xSource = SwDBManager::getDataSourceAsParent(xConnection,aData.sDataSource);

    lcl_InitNumberFormatter(*m_pImpl->pMergeData, xSource);

    assert(pWorkShell);
    pWorkShell->ChgDBData(aData);
    m_bInMerge = true;

    if (IsInitDBFields())
    {
        // with database fields without DB-Name, use DB-Name from Doc
        std::vector<OUString> aDBNames;
        aDBNames.emplace_back();
        SwDBData aInsertData = pWorkShell->GetDBData();
        OUString sDBName = aInsertData.sDataSource
            + OUStringChar(DB_DELIM) + aInsertData.sCommand
            + OUStringChar(DB_DELIM)
            + OUString::number(aInsertData.nCommandType);
        pWorkShell->ChangeDBFields( aDBNames, sDBName);
        SetInitDBFields(false);
    }

    bool bRet = true;
    switch(rMergeDesc.nMergeType)
    {
        case DBMGR_MERGE:
            pWorkShell->StartAllAction();
            pWorkShell->SwViewShell::UpdateFields( true );
            pWorkShell->SetModified();
            pWorkShell->EndAllAction();
            break;

        case DBMGR_MERGE_PRINTER:
        case DBMGR_MERGE_EMAIL:
        case DBMGR_MERGE_FILE:
        case DBMGR_MERGE_SHELL:
            // save files and send them as e-Mail if required
            bRet = MergeMailFiles(pWorkShell, rMergeDesc);
            break;

        default:
            // insert selected entries
            // (was: InsertRecord)
            ImportFromConnection(pWorkShell);
            break;
    }

    m_pImpl->pMergeData.reset();

    if( xWorkObjSh.Is() )
    {
        pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
        xWorkObjSh->DoClose();
    }

    m_bInMerge = false;

    return bRet;
}

void SwDBManager::ImportFromConnection(  SwWrtShell* pSh )
{
    if(!m_pImpl->pMergeData || m_pImpl->pMergeData->bEndOfDB)
        return;

    pSh->StartAllAction();
    pSh->StartUndo();
    bool bGroupUndo(pSh->DoesGroupUndo());
    pSh->DoGroupUndo(false);

    if( pSh->HasSelection() )
        pSh->DelRight();

    std::optional<SwWait> oWait;

    {
        sal_uInt32 i = 0;
        do {

            ImportDBEntry(pSh);
            if( 10 == ++i )
                oWait.emplace( *pSh->GetView().GetDocShell(), true);

        } while(ToNextMergeRecord());
    }

    pSh->DoGroupUndo(bGroupUndo);
    pSh->EndUndo();
    pSh->EndAllAction();
}

void SwDBManager::ImportDBEntry(SwWrtShell* pSh)
{
    if(!m_pImpl->pMergeData || m_pImpl->pMergeData->bEndOfDB)
        return;

    uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
    uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
    OUStringBuffer sStr;
    uno::Sequence<OUString> aColNames = xCols->getElementNames();
    const OUString* pColNames = aColNames.getConstArray();
    tools::Long nLength = aColNames.getLength();
    for(tools::Long i = 0; i < nLength; i++)
    {
        uno::Any aCol = xCols->getByName(pColNames[i]);
        uno::Reference< beans::XPropertySet > xColumnProp;
        aCol >>= xColumnProp;
        SwDBFormatData aDBFormat;
        sStr.append(GetDBField( xColumnProp, aDBFormat));
        if (i < nLength - 1)
            sStr.append("\t");
    }
    pSh->SwEditShell::Insert2(sStr.makeStringAndClear());
    pSh->SwFEShell::SplitNode();    // line feed
}

bool SwDBManager::GetTableNames(weld::ComboBox& rBox, const OUString& rDBName)
{
    bool bRet = false;
    OUString sOldTableName(rBox.get_active_text());
    rBox.clear();
    SwDSParam* pParam = FindDSConnection(rDBName, false);
    uno::Reference< sdbc::XConnection> xConnection;
    if (pParam && pParam->xConnection.is())
        xConnection = pParam->xConnection;
    else
    {
        if ( !rDBName.isEmpty() )
            xConnection = RegisterConnection( rDBName );
    }
    if (xConnection.is())
    {
        uno::Reference<sdbcx::XTablesSupplier> xTSupplier(xConnection, uno::UNO_QUERY);
        if(xTSupplier.is())
        {
            uno::Reference<container::XNameAccess> xTables = xTSupplier->getTables();
            const uno::Sequence<OUString> aTables = xTables->getElementNames();
            for (const OUString& rTable : aTables)
                rBox.append(u"0"_ustr, rTable);
        }
        uno::Reference<sdb::XQueriesSupplier> xQSupplier(xConnection, uno::UNO_QUERY);
        if(xQSupplier.is())
        {
            uno::Reference<container::XNameAccess> xQueries = xQSupplier->getQueries();
            const uno::Sequence<OUString> aQueries = xQueries->getElementNames();
            for (const OUString& rQuery : aQueries)
                rBox.append(u"1"_ustr, rQuery);
        }
        if (!sOldTableName.isEmpty())
            rBox.set_active_text(sOldTableName);
        bRet = true;
    }
    return bRet;
}

// fill Listbox with column names of a database
void SwDBManager::GetColumnNames(weld::ComboBox& rBox,
                             const OUString& rDBName, const OUString& rTableName)
{
    SwDBData aData;
    aData.sDataSource = rDBName;
    aData.sCommand = rTableName;
    aData.nCommandType = -1;
    SwDSParam* pParam = FindDSData(aData, false);
    uno::Reference< sdbc::XConnection> xConnection;
    if(pParam && pParam->xConnection.is())
        xConnection = pParam->xConnection;
    else
    {
        xConnection = RegisterConnection( rDBName );
    }
    GetColumnNames(rBox, xConnection, rTableName);
}

void SwDBManager::GetColumnNames(weld::ComboBox& rBox,
        uno::Reference< sdbc::XConnection> const & xConnection,
        const OUString& rTableName)
{
    rBox.clear();
    uno::Reference< sdbcx::XColumnsSupplier> xColsSupp = SwDBManager::GetColumnSupplier(xConnection, rTableName);
    if(xColsSupp.is())
    {
        uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
        const uno::Sequence<OUString> aColNames = xCols->getElementNames();
        for (const OUString& rColName : aColNames)
        {
            rBox.append_text(rColName);
        }
        ::comphelper::disposeComponent( xColsSupp );
    }
}

SwDBManager::SwDBManager(SwDoc* pDoc)
    : m_aMergeStatus( MergeStatus::Ok )
    , m_bInitDBFields(false)
    , m_bInMerge(false)
    , m_bMergeSilent(false)
    , m_pImpl(new SwDBManager_Impl(*this))
    , m_pMergeEvtSrc(nullptr)
    , m_pDoc(pDoc)
{
}

void SwDBManager::ImplDestroy()
{
    RevokeLastRegistrations();

    // copy required, m_DataSourceParams can be modified while disposing components
    std::vector<uno::Reference<sdbc::XConnection>> aCopiedConnections;
    for (const auto & pParam : m_DataSourceParams)
    {
        if(pParam->xConnection.is())
        {
            aCopiedConnections.push_back(pParam->xConnection);
        }
    }
    for (const auto & xConnection : aCopiedConnections)
    {
        try
        {
            uno::Reference<lang::XComponent> xComp(xConnection, uno::UNO_QUERY);
            if(xComp.is())
                xComp->dispose();
        }
        catch(const uno::RuntimeException&)
        {
            //may be disposed already since multiple entries may have used the same connection
        }
    }
}

SwDBManager::~SwDBManager()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

static void lcl_RemoveSectionLinks( SwWrtShell& rWorkShell )
{
    //reset all links of the sections of synchronized labels
    size_t nSections = rWorkShell.GetSectionFormatCount();
    for (size_t nSection = 0; nSection < nSections; ++nSection)
    {
        SwSectionData aSectionData( *rWorkShell.GetSectionFormat( nSection ).GetSection() );
        if( aSectionData.GetType() == SectionType::FileLink )
        {
            aSectionData.SetType( SectionType::Content );
            aSectionData.SetLinkFileName( OUString() );
            rWorkShell.UpdateSection( nSection, aSectionData );
        }
    }
    rWorkShell.SetLabelDoc( false );
}

static void lcl_SaveDebugDoc( SfxObjectShell *xTargetDocShell,
                              const char *name, int no = 0 )
{
    static OUString sTempDirURL;
    if( sTempDirURL.isEmpty() )
    {
        SvtPathOptions aPathOpt;
        utl::TempFileNamed aTempDir( &aPathOpt.GetTempPath(), true );
        if( aTempDir.IsValid() )
        {
            INetURLObject aTempDirURL( aTempDir.GetURL() );
            sTempDirURL = aTempDirURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
            SAL_INFO( "sw.mailmerge""Dump directory: " << sTempDirURL );
        }
    }
    if( sTempDirURL.isEmpty() )
        return;

    OUString basename = OUString::createFromAscii( name );
    if (no > 0)
        basename += OUString::number(no) + "-";
    // aTempFile is not deleted, but that seems to be intentional
    utl::TempFileNamed aTempFile( basename, true, u".odt", &sTempDirURL );
    INetURLObject aTempFileURL( aTempFile.GetURL() );
    SfxMedium aDstMed(
        aTempFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
        StreamMode::STD_READWRITE );
    bool bAnyError = !xTargetDocShell->DoSaveAs( aDstMed );
    // xObjectShell->DoSaveCompleted crashes the mail merge unit tests, so skip it
    bAnyError |= (ERRCODE_NONE != xTargetDocShell->GetErrorIgnoreWarning());
    if( bAnyError )
        SAL_WARN( "sw.mailmerge""Error saving: " << aTempFile.GetURL() );
    else
        SAL_INFO( "sw.mailmerge""Saved doc as: " << aTempFile.GetURL() );
}

static bool lcl_SaveDoc(
    const INetURLObject* pFileURL,
    const std::shared_ptr<const SfxFilter>& pStoreToFilter,
    const OUString* pStoreToFilterOptions,
    const uno::Sequence< beans::PropertyValue >* pSaveToFilterData,
    const bool bIsPDFexport,
    SfxObjectShell* xObjectShell,
    SwWrtShell& rWorkShell,
    OUString * const decodedURL = nullptr )
{
    OUString url = pFileURL->GetMainURL( INetURLObject::DecodeMechanism::NONE );
    if( decodedURL )
        (*decodedURL) = url;

    SfxMedium* pDstMed = new SfxMedium( url, StreamMode::STD_READWRITE );
    pDstMed->SetFilter( pStoreToFilter );
    if( pStoreToFilterOptions )
        pDstMed->GetItemSet().Put( SfxStringItem(SID_FILE_FILTEROPTIONS,
                                   *pStoreToFilterOptions));
    if( pSaveToFilterData->hasElements() )
        pDstMed->GetItemSet().Put( SfxUnoAnyItem(SID_FILTER_DATA,
                                   uno::Any(*pSaveToFilterData)));

    // convert fields to text if we are exporting to PDF.
    // this prevents a second merge while updating the fields
    // in SwXTextDocument::getRendererCount()
    if( bIsPDFexport )
        rWorkShell.ConvertFieldsToText();

    bool bAnyError = !xObjectShell->DoSaveAs(*pDstMed);
    // Actually this should be a bool... so in case of email and individual
    // files, where this is set, we skip the recently used handling
    bAnyError |= !xObjectShell->DoSaveCompleted( pDstMed, !decodedURL );
    bAnyError |= (ERRCODE_NONE != xObjectShell->GetErrorIgnoreWarning());
    if( bAnyError )
    {
        // error message ??
        ErrorHandler::HandleError( xObjectShell->GetErrorIgnoreWarning() );
    }
    return !bAnyError;
}

static void lcl_PreparePrinterOptions(
    const uno::Sequence< beans::PropertyValue >& rInPrintOptions,
    uno::Sequence< beans::PropertyValue >& rOutPrintOptions)
{
    // printing should be done synchronously otherwise the document
    // might already become invalid during the process

    rOutPrintOptions = { comphelper::makePropertyValue(u"Wait"_ustr, true) };

    // copy print options
    sal_Int32 nIndex = 1;
    forconst beans::PropertyValue& rOption : rInPrintOptions)
    {
        if( rOption.Name == "CopyCount" || rOption.Name == "FileName"
            || rOption.Name == "Collate" || rOption.Name == "Pages"
            || rOption.Name == "Wait" || rOption.Name == "PrinterName" )
        {
            // add an option
            rOutPrintOptions.realloc( nIndex + 1 );
            auto pOutPrintOptions = rOutPrintOptions.getArray();
            pOutPrintOptions[ nIndex ].Name = rOption.Name;
            pOutPrintOptions[ nIndex++ ].Value = rOption.Value ;
        }
    }
}

static void lcl_PrepareSaveFilterDataOptions(
    const uno::Sequence< beans::PropertyValue >& rInSaveFilterDataptions,
    uno::Sequence< beans::PropertyValue >& rOutSaveFilterDataOptions,
    const OUString& sPassword)
{
    rOutSaveFilterDataOptions
        = { comphelper::makePropertyValue(u"EncryptFile"_ustr, true),
            comphelper::makePropertyValue(u"DocumentOpenPassword"_ustr, sPassword) };

    // copy other options
    sal_Int32 nIndex = 2;
    forconst beans::PropertyValue& rOption : rInSaveFilterDataptions)
    {
         rOutSaveFilterDataOptions.realloc( nIndex + 1 );
         auto pOutSaveFilterDataOptions = rOutSaveFilterDataOptions.getArray();
         pOutSaveFilterDataOptions[ nIndex ].Name = rOption.Name;
         pOutSaveFilterDataOptions[ nIndex++ ].Value = rOption.Value ;
    }

}


static SfxObjectShell* lcl_CreateWorkingDocument(
    // input
    const WorkingDocType aType, const SwWrtShell &rSourceWrtShell,
    // optional input
    const vcl::Window *pSourceWindow,
    // optional in and output to swap the DB manager
    SwDBManager** const ppDBManager,
    // optional output
    SwView** const pView, SwWrtShell** const pWrtShell, rtl::Reference<SwDoc>* const pDoc )
{
    const SwDoc *pSourceDoc = rSourceWrtShell.GetDoc();
    SfxObjectShellRef xWorkObjectShell = pSourceDoc->CreateCopy( true, (aType == WorkingDocType::TARGET) );
    SfxViewFrame* pWorkFrame = SfxViewFrame::LoadHiddenDocument( *xWorkObjectShell, SFX_INTERFACE_NONE );

    if( pSourceWindow )
    {
        // the created window has to be located at the same position as the source window
        vcl::Window& rTargetWindow = pWorkFrame->GetFrame().GetWindow();
        rTargetWindow.SetPosPixel( pSourceWindow->GetPosPixel() );
    }

    SwView* pWorkView = static_cast< SwView* >( pWorkFrame->GetViewShell() );

    if (SwWrtShell* pWorkWrtShell = pWorkView->GetWrtShellPtr())
    {
        pWorkWrtShell->GetViewOptions()->SetIdle( false );
        pWorkView->AttrChangedNotify(nullptr);// in order for SelectShell to be called
        SwDoc* pWorkDoc = pWorkWrtShell->GetDoc();
        pWorkDoc->GetIDocumentUndoRedo().DoUndo( false );
        pWorkDoc->ReplaceDocumentProperties( *pSourceDoc );

        // import print settings
        const SwPrintData &rPrintData = pSourceDoc->getIDocumentDeviceAccess().getPrintData();
        pWorkDoc->getIDocumentDeviceAccess().setPrintData(rPrintData);
        const JobSetup *pJobSetup = pSourceDoc->getIDocumentDeviceAccess().getJobsetup();
        if (pJobSetup)
            pWorkDoc->getIDocumentDeviceAccess().setJobsetup(*pJobSetup);

        if( aType == WorkingDocType::TARGET )
        {
            assert( !ppDBManager );
            pWorkDoc->SetInMailMerge( true );
            pWorkWrtShell->SetLabelDoc( false );
        }
        else
        {
            // We have to swap the DBmanager of the new doc, so we also need input
            assert(ppDBManager && *ppDBManager);
            SwDBManager *pWorkDBManager = pWorkDoc->GetDBManager();
            pWorkDoc->SetDBManager( *ppDBManager );
            *ppDBManager = pWorkDBManager;

            if( aType == WorkingDocType::SOURCE )
            {
                // the GetDBData call constructs the data, if it's missing - kind of const...
                pWorkWrtShell->ChgDBData( const_cast<SwDoc*>(pSourceDoc)->GetDBData() );
                // some DocumentSettings are currently not copied by SwDoc::CreateCopy
                pWorkWrtShell->SetLabelDoc( rSourceWrtShell.IsLabelDoc() );
                pWorkDoc->getIDocumentState().ResetModified();
            }
            else
                pWorkDoc->getIDocumentLinksAdministration().EmbedAllLinks();
        }

        if( pView )     *pView     = pWorkView;
        if( pWrtShell ) *pWrtShell = pWorkWrtShell;
        if( pDoc )      *pDoc      = pWorkDoc;
    }

    return xWorkObjectShell.get();
}

static rtl::Reference<SwMailMessage> lcl_CreateMailFromDoc(
    const SwMergeDescriptor &rMergeDescriptor,
    const OUString &sFileURL, const OUString &sMailRecipient,
    const OUString &sMailBodyMimeType, rtl_TextEncoding sMailEncoding,
    const OUString &sAttachmentMimeType )
{
    rtl::Reference<SwMailMessage> pMessage = new SwMailMessage;
    if( rMergeDescriptor.pMailMergeConfigItem->IsMailReplyTo() )
        pMessage->setReplyToAddress(rMergeDescriptor.pMailMergeConfigItem->GetMailReplyTo());
    pMessage->addRecipient( sMailRecipient );
    pMessage->SetSenderAddress( rMergeDescriptor.pMailMergeConfigItem->GetMailAddress() );

    OUStringBuffer sBody;
    if( rMergeDescriptor.bSendAsAttachment )
    {
        sBody = rMergeDescriptor.sMailBody;
        mail::MailAttachment aAttach;
        aAttach.Data = new SwMailTransferable( sFileURL,
            rMergeDescriptor.sAttachmentName, sAttachmentMimeType );
        aAttach.ReadableName = rMergeDescriptor.sAttachmentName;
        pMessage->addAttachment( aAttach );
    }
    else
    {
        //read in the temporary file and use it as mail body
        SfxMedium aMedium( sFileURL, StreamMode::READ );
        SvStream* pInStream = aMedium.GetInStream();
        assert( pInStream && "no output file created?" );
        if( !pInStream )
            return pMessage;

        pInStream->SetStreamCharSet( sMailEncoding );
        OStringBuffer sLine;
        while ( pInStream->ReadLine( sLine ) )
        {
            sBody.append(OStringToOUString( sLine, sMailEncoding ) + "\n");
        }
    }
    pMessage->setSubject( rMergeDescriptor.sSubject );
    uno::Reference< datatransfer::XTransferable> xBody =
                new SwMailTransferable( sBody.makeStringAndClear(), sMailBodyMimeType );
    pMessage->setBody( xBody );

    forconst OUString& sCcRecipient : rMergeDescriptor.aCopiesTo )
        pMessage->addCcRecipient( sCcRecipient );
    forconst OUString& sBccRecipient : rMergeDescriptor.aBlindCopiesTo )
        pMessage->addBccRecipient( sBccRecipient );

    return pMessage;
}

class SwDBManager::MailDispatcherListener_Impl : public IMailDispatcherListener
{
    SwDBManager &m_rDBManager;

public:
    explicit MailDispatcherListener_Impl( SwDBManager &rDBManager )
        : m_rDBManager( rDBManager ) {}

    virtual void idle() override {}

    virtual void mailDelivered( uno::Reference< mail::XMailMessage> xMessage ) override
    {
        std::unique_lock aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
        if ( m_rDBManager.m_pImpl->m_xLastMessage == xMessage )
            m_rDBManager.m_pImpl->m_xLastMessage.clear();
    }

    virtual void mailDeliveryError( ::rtl::Reference<MailDispatcher> xMailDispatcher,
                uno::Reference< mail::XMailMessage>, const OUString& ) override
    {
        std::unique_lock aGuard( m_rDBManager.m_pImpl->m_aAllEmailSendMutex );
        m_rDBManager.m_aMergeStatus = MergeStatus::Error;
        m_rDBManager.m_pImpl->m_xLastMessage.clear();
        xMailDispatcher->stop();
    }
};

/**
 * Please have a look at the README in the same directory, before you make
 * larger changes in this function!
 */

bool SwDBManager::MergeMailFiles(SwWrtShell* pSourceShell,
                                 const SwMergeDescriptor& rMergeDescriptor)
{
    // deconstruct mail merge type for better readability.
    // uppercase naming is intentional!
    const bool bMT_EMAIL   = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL;
    const bool bMT_SHELL   = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL;
    const bool bMT_PRINTER = rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER;
    const bool bMT_FILE    = rMergeDescriptor.nMergeType == DBMGR_MERGE_FILE;

    //check if the doc is synchronized and contains at least one linked section
    const bool bSynchronizedDoc = pSourceShell->IsLabelDoc() && pSourceShell->GetSectionFormatCount() > 1;
    const bool bNeedsTempFiles = ( bMT_EMAIL || bMT_FILE );
    const bool bIsMergeSilent = IsMergeSilent();

    bool bCheckSingleFile_ = rMergeDescriptor.bCreateSingleFile;
    OUString sPrefix_ = rMergeDescriptor.sPrefix;
    if( bMT_EMAIL )
    {
        assert( !rMergeDescriptor.bPrefixIsFilename );
        assert(!bCheckSingleFile_);
        bCheckSingleFile_ = false;
    }
    else if( bMT_SHELL || bMT_PRINTER )
    {
        assert(bCheckSingleFile_);
        bCheckSingleFile_ = true;
        assert(sPrefix_.isEmpty());
        sPrefix_.clear();
    }
    const bool bCreateSingleFile = bCheckSingleFile_;
    const OUString sDescriptorPrefix = sPrefix_;

    // Setup for dumping debugging documents
    static const sal_Int32 nMaxDumpDocs = []() {
        if (const char* sEnv = getenv("SW_DEBUG_MAILMERGE_DOCS"))
            return OUString(sEnv, strlen(sEnv), osl_getThreadTextEncoding()).toInt32();
        else
            return sal_Int32(0);
    }();

    ::rtl::Reference< MailDispatcher >          xMailDispatcher;
    ::rtl::Reference< IMailDispatcherListener > xMailListener;
    OUString                            sMailBodyMimeType;
    rtl_TextEncoding                    sMailEncoding = ::osl_getThreadTextEncoding();

    uno::Reference< beans::XPropertySet > xColumnProp;
    uno::Reference< beans::XPropertySet > xPasswordColumnProp;

    // Check for (mandatory) email or (optional) filename column
    SwDBFormatData aColumnDBFormat;
    bool bColumnName = !rMergeDescriptor.sDBcolumn.isEmpty();
    bool bPasswordColumnName = !rMergeDescriptor.sDBPasswordColumn.isEmpty();

    if( ! bColumnName )
    {
        if( bMT_EMAIL )
            return false;
    }
    else
    {
        uno::Reference< sdbcx::XColumnsSupplier > xColsSupp( m_pImpl->pMergeData->xResultSet, uno::UNO_QUERY );
        uno::Reference<container::XNameAccess> xCols = xColsSupp->getColumns();
        if( !xCols->hasByName( rMergeDescriptor.sDBcolumn ) )
            return false;
        uno::Any aCol = xCols->getByName( rMergeDescriptor.sDBcolumn );
        aCol >>= xColumnProp;

        if(bPasswordColumnName)
        {
            aCol = xCols->getByName( rMergeDescriptor.sDBPasswordColumn );
            aCol >>= xPasswordColumnProp;
        }

        aColumnDBFormat.xFormatter = m_pImpl->pMergeData->xFormatter;
        aColumnDBFormat.aNullDate  = m_pImpl->pMergeData->aNullDate;

        if( bMT_EMAIL )
        {
            // Reset internal mail accounting data
            {
                std::unique_lock aGuard(m_pImpl->m_aAllEmailSendMutex);
                m_pImpl->m_xLastMessage.clear();
            }

            xMailDispatcher.set( new MailDispatcher(rMergeDescriptor.xSmtpServer) );
            xMailListener = new MailDispatcherListener_Impl( *this );
            xMailDispatcher->addListener( xMailListener );
            if(!rMergeDescriptor.bSendAsAttachment && rMergeDescriptor.bSendAsHTML)
            {
                sMailBodyMimeType = u"text/html; charset=utf-8"_ustr;
                sMailEncoding = RTL_TEXTENCODING_UTF8;
            }
            else
                sMailBodyMimeType = u"text/plain; charset=UTF-8; format=flowed"_ustr;
        }
    }

    SwDocShell  *pSourceDocSh = pSourceShell->GetView().GetDocShell();

    // setup the output format
    std::shared_ptr<const SfxFilter> pStoreToFilter = SwIoSystem::GetFileFilter(
        pSourceDocSh->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
    SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
    const OUString* pStoreToFilterOptions = nullptr;

    // if a save_to filter is set then use it - otherwise use the default
    if( bMT_EMAIL && !rMergeDescriptor.bSendAsAttachment )
    {
        OUString sExtension = rMergeDescriptor.bSendAsHTML ? u"html"_ustr : u"txt"_ustr;
        pStoreToFilter = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
    }
    else if( !rMergeDescriptor.sSaveToFilter.isEmpty())
    {
        std::shared_ptr<const SfxFilter> pFilter =
                pFilterContainer->GetFilter4FilterName( rMergeDescriptor.sSaveToFilter );
        if(pFilter)
        {
            pStoreToFilter = std::move(pFilter);
            if(!rMergeDescriptor.sSaveToFilterOptions.isEmpty())
                pStoreToFilterOptions = &rMergeDescriptor.sSaveToFilterOptions;
        }
    }
    const bool bIsPDFexport = pStoreToFilter && pStoreToFilter->GetFilterName() == "writer_pdf_Export";
    const bool bIsMultiFile = bMT_FILE && !bCreateSingleFile;

    m_aMergeStatus = MergeStatus::Ok;

    // in case of creating a single resulting file this has to be created here
    SwView*           pTargetView     = rMergeDescriptor.pMailMergeConfigItem ?
                                        rMergeDescriptor.pMailMergeConfigItem->GetTargetView() : nullptr;
    SwWrtShell*       pTargetShell    = nullptr;
    SfxObjectShellRef xTargetDocShell;
    rtl::Reference<SwDoc> pTargetDoc;

    std::unique_ptr< utl::TempFileNamed > aTempFile;
    sal_uInt16 nStartingPageNo = 0;

    std::shared_ptr<weld::GenericDialogController> xProgressDlg;

    comphelper::ScopeGuard restoreInMailMerge(
        [pSourceShell, val = pSourceShell->GetDoc()->IsInMailMerge()]
        { pSourceShell->GetDoc()->SetInMailMerge(val); });
    pSourceShell->GetDoc()->SetInMailMerge(true);
    try
    {
        vcl::Window *pSourceWindow = nullptr;
        if( !bIsMergeSilent )
        {
            // construct the process dialog
            pSourceWindow = &pSourceShell->GetView().GetEditWin();
            if (!bMT_PRINTER)
                xProgressDlg = std::make_shared<CreateMonitor>(pSourceWindow->GetFrameWeld());
            else
            {
                xProgressDlg = std::make_shared<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 );

            if (nMaxDumpDocs)
                lcl_SaveDebugDoc( xTargetDocShell.get(), "MergeDoc" );
        }
        else if( pTargetView )
        {
            pTargetShell = pTargetView->GetWrtShellPtr();
            if (pTargetShell)
            {
                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, m_pImpl->pMergeData.get() );

            // 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);
        }

        sal_Int32 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;
        rtl::Reference<SwDoc> pWorkDoc;
        SwDBManager*       pWorkDocOrigDBManager = nullptr;
        SwWrtShell*        pWorkShell            = nullptr;
        bool               bWorkDocInitialized   = false;

        do
        {
            nStartRow = m_pImpl->pMergeData ? m_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 = u"_"_ustr;
                }
                else
                {
                    INetURLObject aEntry( sPrefix );
                    sLeading = aEntry.GetBase();
                    aEntry.removeSegment();
                    sPrefix = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
                }

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

            uno::Sequence< beans::PropertyValue > aSaveToFilterDataOptions( rMergeDescriptor.aSaveToFilterData );

            if( bMT_EMAIL || bPasswordColumnName )
            {
                OUString sPasswordColumnData = GetDBField( xPasswordColumnProp, aColumnDBFormat );
                lcl_PrepareSaveFilterDataOptions( rMergeDescriptor.aSaveToFilterData, aSaveToFilterDataOptions, sPasswordColumnData );
            }

            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) + " " + OUString::number( nDocNo );
                        pPrintMonDlg->m_xPrintInfo->set_label(sStat);
                    }
                    //TODO xProgressDlg->queue_draw();
                }

                Scheduler::ProcessEventsToIdle();

                // 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 || bIsMultiFile )
                {
                    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)
                {
                    rtl::Reference< SwXMailMerge > xRef(
                        const_cast<SwXMailMerge*>(pEvtSrc) );
                    text::MailMergeEvent aEvt( static_cast<text::XMailMergeBroadcaster*>(xRef.get()), xWorkDocSh->GetModel() );
                    SolarMutexReleaser rel;
                    xRef->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();
                    // remove of invisible content has influence on page count and so on fields for page count,
                    // therefore layout has to be updated before fields are converted to text
                    pWorkShell->CalcLayout();
                    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);
                    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(
                            SwPaM(appendedDocStart), SwMarkName(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
                            ::sw::mark::InsertMode::New);
                        aMergeInfo.nDBRow = nStartRow;
                        rMergeDescriptor.pMailMergeConfigItem->AddMergedDocument( aMergeInfo );
                    }
                }
                else
                {
                    assert( bNeedsTempFiles );
                    assert( pWorkShell->IsExpFieldsLocked() );

                    if (bIsMultiFile && pWorkDoc->HasInvisibleContent())
                    {
                        pWorkDoc->RemoveInvisibleContent();
                        pWorkShell->CalcLayout();
                        pWorkShell->ConvertFieldsToText();
                    }

                    // 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,
                                    &aSaveToFilterDataOptions, 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
                        {
                            rtl::Reference< SwMailMessage > xMessage = lcl_CreateMailFromDoc(
                                rMergeDescriptor, sFileURL, sColumnData, sMailBodyMimeType,
                                sMailEncoding, pStoreToFilter->GetMimeType() );
                            if( xMessage.is() )
                            {
                                std::unique_lock aGuard( m_pImpl->m_aAllEmailSendMutex );
                                m_pImpl->m_xLastMessage.set( xMessage );
                                xMailDispatcher->enqueueMailMessage( xMessage );
                                if( !xMailDispatcher->isStarted() )
                                    xMailDispatcher->start();
                            }
                        }
                    }
                }
                if( bCreateSingleFile || bIsPDFexport || bIsMultiFile)
                {
                    pWorkDoc->SetDBManager( pWorkDocOrigDBManager );
                    pWorkDoc.clear();
                    xWorkDocSh->DoClose();
                    xWorkDocSh = nullptr;
                }
            }

            bWorkDocInitialized = true;
            nDocNo++;
            nEndRow = m_pImpl->pMergeData ? m_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 )
            {
                if (pWorkDoc)
                    pWorkDoc->SetDBManager(pWorkDocOrigDBManager);
                if (xWorkDocSh.Is())
                    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 );

            if( IsMergeOk() && bMT_FILE )
            {
                // save merged document
                assert( aTempFile );
                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;
                }
            }
            else if( IsMergeOk() && bMT_PRINTER )
            {
                // print the target document
                uno::Sequence< beans::PropertyValue > aOptions( rMergeDescriptor.aPrintOptions );
                lcl_PreparePrinterOptions( rMergeDescriptor.aPrintOptions, aOptions );
                pTargetView->ExecPrint( aOptions, bIsMergeSilent, false/*bPrintAsync*/ );
            }
        }

        // 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);
        }

        SwModule::get()->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("sw::SwDBManager aEmailDispatcherPollTimer");
                aEmailDispatcherPollTimer.SetTimeout( 500 );
                aEmailDispatcherPollTimer.Start();
                while( IsMergeOk() && m_pImpl->m_xLastMessage.is() && !Application::IsQuit())
                    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!
        forconst OUString &sFileURL : aFilesToRemove )
            SWUnoHelper::UCB_DeleteFile( sFileURL );
    }
    catch (const uno::Exception&)
    {
        if (xProgressDlg)
        {
            xProgressDlg->response(RET_CANCEL);
        }
    }

    return !IsMergeError();
}

void SwDBManager::MergeCancel()
{
    if (m_aMergeStatus < MergeStatus::Cancel)
        m_aMergeStatus = MergeStatus::Cancel;
}

// determine the column's Numberformat and transfer to the forwarded Formatter,
// if applicable.
sal_uInt32 SwDBManager::GetColumnFormat( const OUString& rDBName,
                                const OUString& rTableName,
                                const OUString& rColNm,
                                SvNumberFormatter* pNFormatr,
                                LanguageType nLanguage )
{
    sal_uInt32 nRet = 0;
    if(pNFormatr)
    {
        uno::Reference< sdbc::XDataSource> xSource;
        uno::Reference< sdbc::XConnection> xConnection;
        bool bUseMergeData = false;
        uno::Reference< sdbcx::XColumnsSupplier> xColsSupp;
        bool bDisposeConnection = false;
        if(m_pImpl->pMergeData &&
            ((m_pImpl->pMergeData->sDataSource == rDBName && m_pImpl->pMergeData->sCommand == rTableName) ||
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=94 G=92

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.