summaryrefslogtreecommitdiffstats
path: root/sw/source/ui/dbui
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/ui/dbui')
-rw-r--r--sw/source/ui/dbui/addresslistdialog.cxx642
-rw-r--r--sw/source/ui/dbui/addresslistdialog.hxx101
-rw-r--r--sw/source/ui/dbui/createaddresslistdialog.cxx589
-rw-r--r--sw/source/ui/dbui/createaddresslistdialog.hxx108
-rw-r--r--sw/source/ui/dbui/customizeaddresslistdialog.cxx181
-rw-r--r--sw/source/ui/dbui/customizeaddresslistdialog.hxx91
-rw-r--r--sw/source/ui/dbui/dbinsdlg.cxx1748
-rw-r--r--sw/source/ui/dbui/dbtablepreviewdialog.cxx118
-rw-r--r--sw/source/ui/dbui/dbtablepreviewdialog.hxx46
-rw-r--r--sw/source/ui/dbui/mailmergewizard.cxx265
-rw-r--r--sw/source/ui/dbui/mmaddressblockpage.cxx1581
-rw-r--r--sw/source/ui/dbui/mmaddressblockpage.hxx284
-rw-r--r--sw/source/ui/dbui/mmdocselectpage.cxx226
-rw-r--r--sw/source/ui/dbui/mmdocselectpage.hxx58
-rw-r--r--sw/source/ui/dbui/mmgreetingspage.cxx422
-rw-r--r--sw/source/ui/dbui/mmgreetingspage.hxx131
-rw-r--r--sw/source/ui/dbui/mmlayoutpage.cxx692
-rw-r--r--sw/source/ui/dbui/mmlayoutpage.hxx87
-rw-r--r--sw/source/ui/dbui/mmoutputtypepage.cxx532
-rw-r--r--sw/source/ui/dbui/mmoutputtypepage.hxx43
-rw-r--r--sw/source/ui/dbui/mmresultdialogs.cxx1295
-rw-r--r--sw/source/ui/dbui/selectdbtabledialog.cxx143
-rw-r--r--sw/source/ui/dbui/selectdbtabledialog.hxx44
23 files changed, 9427 insertions, 0 deletions
diff --git a/sw/source/ui/dbui/addresslistdialog.cxx b/sw/source/ui/dbui/addresslistdialog.cxx
new file mode 100644
index 000000000..26e8c9e96
--- /dev/null
+++ b/sw/source/ui/dbui/addresslistdialog.cxx
@@ -0,0 +1,642 @@
+/* -*- 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 <swtypes.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include "addresslistdialog.hxx"
+#include "selectdbtabledialog.hxx"
+#include "createaddresslistdialog.hxx"
+#include <mailmergewizard.hxx>
+#include <mmconfigitem.hxx>
+#include "mmaddressblockpage.hxx"
+#include <dbmgr.hxx>
+#include <dbconfig.hxx>
+#include <unotools/tempfile.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <com/sun/star/sdbc/XCloseable.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/FilterDialog.hpp>
+#include <com/sun/star/sdb/XDocumentDataSource.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <o3tl/safeint.hxx>
+#include <swunohelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <tools/diagnose_ex.h>
+#include <svl/urihelper.hxx>
+#include <strings.hrc>
+#include <view.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::ui::dialogs;
+
+struct AddressUserData_Impl
+{
+ uno::Reference<XDataSource> xSource;
+ SharedConnection xConnection;
+ uno::Reference< XColumnsSupplier> xColumnsSupplier;
+ uno::Reference< sdbc::XResultSet> xResultSet;
+ OUString sFilter;
+ OUString sURL; // data is editable
+ sal_Int32 nCommandType;
+ sal_Int32 nTableAndQueryCount;
+ AddressUserData_Impl() :
+ nCommandType(0),
+ nTableAndQueryCount(-1)
+ {}
+};
+
+static OUString lcl_getFlatURL( uno::Reference<beans::XPropertySet> const & xSourceProperties )
+{
+ if(xSourceProperties.is())
+ {
+ OUString sDBURL;
+ xSourceProperties->getPropertyValue("URL") >>= sDBURL;
+ if (sDBURL.startsWith("sdbc:flat:"))
+ {
+ uno::Sequence<OUString> aFilters;
+ xSourceProperties->getPropertyValue("TableFilter") >>= aFilters;
+ uno::Sequence<PropertyValue> aInfo;
+ xSourceProperties->getPropertyValue("Info") >>= aInfo;
+ if(aFilters.getLength() == 1 && aInfo.hasElements() )
+ {
+ OUString sExtension;
+ OUString sCharSet;
+ for(const auto& rInfo : std::as_const(aInfo))
+ {
+ if(rInfo.Name == "Extension")
+ rInfo.Value >>= sExtension;
+ else if(rInfo.Name == "CharSet")
+ rInfo.Value >>= sCharSet;
+ }
+ if (sCharSet=="UTF-8")
+ {
+ //#i97577# at this point the 'URL' can also be a file name!
+ return URIHelper::SmartRel2Abs( INetURLObject(), sDBURL.copy(10) )
+ + "/" + aFilters[0] + "." + sExtension;
+ }
+ }
+ }
+ }
+ return OUString();
+}
+
+SwAddressListDialog::SwAddressListDialog(SwMailMergeAddressBlockPage* pParent)
+ : SfxDialogController(pParent->GetWizard()->getDialog(), "modules/swriter/ui/selectaddressdialog.ui", "SelectAddressDialog")
+ , m_bInSelectHdl(false)
+ , m_pAddressPage(pParent)
+ , m_xDescriptionFI(m_xBuilder->weld_label("desc"))
+ , m_xConnecting(m_xBuilder->weld_label("connecting"))
+ , m_xListLB(m_xBuilder->weld_tree_view("sources"))
+ , m_xLoadListPB(m_xBuilder->weld_button("add"))
+ , m_xRemovePB(m_xBuilder->weld_button("remove"))
+ , m_xCreateListPB(m_xBuilder->weld_button("create"))
+ , m_xFilterPB(m_xBuilder->weld_button("filter"))
+ , m_xEditPB(m_xBuilder->weld_button("edit"))
+ , m_xTablePB(m_xBuilder->weld_button("changetable"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+ , m_xIter(m_xListLB->make_iterator())
+{
+ m_sConnecting = m_xConnecting->get_label();
+
+ const OUString sTemp(m_xDescriptionFI->get_label()
+ .replaceFirst("%1", m_xLoadListPB->strip_mnemonic(m_xLoadListPB->get_label()))
+ .replaceFirst("%2", m_xCreateListPB->strip_mnemonic(m_xCreateListPB->get_label())));
+ m_xDescriptionFI->set_label(sTemp);
+ m_xFilterPB->connect_clicked( LINK( this, SwAddressListDialog, FilterHdl_Impl ));
+ m_xLoadListPB->connect_clicked( LINK( this, SwAddressListDialog, LoadHdl_Impl ));
+ m_xRemovePB->connect_clicked( LINK(this, SwAddressListDialog, RemoveHdl_Impl ));
+ m_xCreateListPB->connect_clicked( LINK( this, SwAddressListDialog,CreateHdl_Impl ));
+ m_xEditPB->connect_clicked(LINK( this, SwAddressListDialog, EditHdl_Impl));
+ m_xTablePB->connect_clicked(LINK( this, SwAddressListDialog, TableSelectHdl_Impl));
+
+ m_xListLB->set_size_request(m_xListLB->get_approximate_digit_width() * 52,
+ m_xListLB->get_height_rows(9));
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xListLB->get_approximate_digit_width() * 26)
+ };
+ m_xListLB->set_column_fixed_widths(aWidths);
+
+ m_xListLB->make_sorted();
+ m_xOK->connect_clicked(LINK(this, SwAddressListDialog, OKHdl_Impl));
+
+ uno::Reference<XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+ m_xDBContext = DatabaseContext::create(xContext);
+
+ SwMailMergeConfigItem& rConfigItem = m_pAddressPage->GetWizard()->GetConfigItem();
+ const SwDBData& rCurrentData = rConfigItem.GetCurrentDBData();
+
+ bool bEnableEdit = false;
+ bool bEnableOK = true;
+ bool bSelected = false;
+ m_xListLB->unselect_all();
+
+ SwDBConfig aDb;
+ const OUString sBibliography = aDb.GetBibliographySource().sDataSource;
+ const uno::Sequence< OUString> aNames = m_xDBContext->getElementNames();
+ for(const OUString& rName : aNames)
+ {
+ if ( rName == sBibliography )
+ continue;
+ m_xListLB->append(m_xIter.get());
+ m_xListLB->set_text(*m_xIter, rName, 0);
+ m_aUserData.emplace_back(new AddressUserData_Impl);
+ AddressUserData_Impl* pUserData = m_aUserData.back().get();
+ m_xListLB->set_id(*m_xIter, weld::toId(pUserData));
+ if (rName == rCurrentData.sDataSource)
+ {
+ m_xListLB->select(*m_xIter);
+ bSelected = true;
+ m_xListLB->set_text(*m_xIter, rCurrentData.sCommand, 1);
+ pUserData->nCommandType = rCurrentData.nCommandType;
+ pUserData->xSource = rConfigItem.GetSource();
+ pUserData->xConnection = rConfigItem.GetConnection();
+ pUserData->xColumnsSupplier = rConfigItem.GetColumnsSupplier();
+ pUserData->xResultSet = rConfigItem.GetResultSet();
+ pUserData->sFilter = rConfigItem.GetFilter();
+ //is the data source editable (csv, Unicode, single table)
+ uno::Reference<beans::XPropertySet> xSourceProperties;
+ try
+ {
+ m_xDBContext->getByName(rName) >>= xSourceProperties;
+ pUserData->sURL = lcl_getFlatURL( xSourceProperties );
+ bEnableEdit = !pUserData->sURL.isEmpty() &&
+ SWUnoHelper::UCB_IsFile( pUserData->sURL ) && //#i97577#
+ !SWUnoHelper::UCB_IsReadOnlyFileName( pUserData->sURL );
+ }
+ catch (const uno::Exception&)
+ {
+ bEnableOK = false;
+ }
+ m_aDBData = rCurrentData;
+ }
+ }
+
+ bool bHasChildren = m_xListLB->n_children() > 0;
+ if (bHasChildren && !bSelected)
+ m_xListLB->select(0); // select the first entry if nothing else selected
+ m_xOK->set_sensitive(bHasChildren && bEnableOK);
+ m_xEditPB->set_sensitive(bEnableEdit);
+ m_xRemovePB->set_sensitive(m_xListLB->n_children() > 0);
+ m_xFilterPB->set_sensitive(m_xListLB->n_children() > 0);
+ m_xTablePB->set_sensitive(m_xListLB->n_children() > 0);
+ m_xListLB->connect_changed(LINK(this, SwAddressListDialog, ListBoxSelectHdl_Impl));
+ TableSelectHdl(nullptr);
+}
+
+SwAddressListDialog::~SwAddressListDialog()
+{
+}
+
+IMPL_LINK_NOARG(SwAddressListDialog, FilterHdl_Impl, weld::Button&, void)
+{
+ int nSelect = m_xListLB->get_selected_index();
+ uno::Reference< XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
+ if (nSelect == -1)
+ return;
+
+ const OUString sCommand = m_xListLB->get_text(nSelect, 1);
+ if (sCommand.isEmpty())
+ return;
+
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ if (!pUserData->xConnection.is() )
+ return;
+
+ try
+ {
+ uno::Reference<lang::XMultiServiceFactory> xConnectFactory(pUserData->xConnection, UNO_QUERY_THROW);
+ uno::Reference<XSingleSelectQueryComposer> xComposer(
+ xConnectFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"), UNO_QUERY_THROW);
+
+ uno::Reference<XRowSet> xRowSet(
+ xMgr->createInstance("com.sun.star.sdb.RowSet"), UNO_QUERY);
+ uno::Reference<XPropertySet> xRowProperties(xRowSet, UNO_QUERY);
+ xRowProperties->setPropertyValue("DataSourceName",
+ Any(m_xListLB->get_text(nSelect, 0)));
+ xRowProperties->setPropertyValue("Command", Any(sCommand));
+ xRowProperties->setPropertyValue("CommandType", Any(pUserData->nCommandType));
+ xRowProperties->setPropertyValue("ActiveConnection", Any(pUserData->xConnection.getTyped()));
+ xRowSet->execute();
+
+ OUString sQuery;
+ xRowProperties->getPropertyValue("ActiveCommand")>>= sQuery;
+ xComposer->setQuery(sQuery);
+ if(!pUserData->sFilter.isEmpty())
+ xComposer->setFilter(pUserData->sFilter);
+
+ uno::Reference< XExecutableDialog> xDialog = sdb::FilterDialog::createWithQuery( comphelper::getComponentContext(xMgr),
+ xComposer,xRowSet, uno::Reference<awt::XWindow>() );
+
+ if ( RET_OK == xDialog->execute() )
+ {
+ weld::WaitObject aWait(m_xDialog.get());
+ pUserData->sFilter = xComposer->getFilter();
+ }
+ ::comphelper::disposeComponent(xRowSet);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "exception caught in SwAddressListDialog::FilterHdl_Impl");
+ }
+}
+
+IMPL_LINK_NOARG(SwAddressListDialog, LoadHdl_Impl, weld::Button&, void)
+{
+ SwView* pView = m_pAddressPage->GetWizard()->GetSwView();
+
+ const OUString sNewSource = SwDBManager::LoadAndRegisterDataSource(m_xDialog.get(), pView ? pView->GetDocShell() : nullptr);
+ if(!sNewSource.isEmpty())
+ {
+ m_xListLB->append(m_xIter.get());
+ m_xListLB->set_text(*m_xIter, sNewSource, 0);
+ m_aUserData.emplace_back(new AddressUserData_Impl);
+ AddressUserData_Impl* pUserData = m_aUserData.back().get();
+ m_xListLB->set_id(*m_xIter, weld::toId(pUserData));
+ m_xListLB->select(*m_xIter);
+ ListBoxSelectHdl_Impl(*m_xListLB);
+ m_xRemovePB->set_sensitive(true);
+ }
+}
+
+IMPL_LINK_NOARG(SwAddressListDialog, RemoveHdl_Impl, weld::Button&, void)
+{
+ int nEntry = m_xListLB->get_selected_index();
+ if (nEntry == -1)
+ return;
+
+ std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(getDialog(),
+ VclMessageType::Question, VclButtonsType::YesNo, SwResId(ST_DELETE_CONFIRM)));
+ if (xQuery->run() != RET_YES)
+ return;
+
+ // Remove data source connection
+ SwDBManager::RevokeDataSource(m_xListLB->get_selected_text());
+ // Remove item from the list
+ m_xListLB->remove(nEntry);
+ // If this was the last item, disable the Remove & Edit buttons and enable Create
+ if (m_xListLB->n_children() < 1 )
+ {
+ m_xRemovePB->set_sensitive(false);
+ m_xEditPB->set_sensitive(false);
+ m_xFilterPB->set_sensitive(false);
+ m_xCreateListPB->set_sensitive(true);
+ }
+
+
+}
+
+IMPL_LINK_NOARG(SwAddressListDialog, CreateHdl_Impl, weld::Button&, void)
+{
+ SwCreateAddressListDialog aDlg(m_xDialog.get(), /*sInputURL*/OUString(), m_pAddressPage->GetWizard()->GetConfigItem());
+ if (RET_OK != aDlg.run())
+ return;
+
+ //register the URL a new datasource
+ const OUString sURL = aDlg.GetURL();
+ try
+ {
+ uno::Reference<XInterface> xNewInstance = m_xDBContext->createInstance();
+ INetURLObject aURL( sURL );
+ const OUString sNewName = aURL.getBase();
+ //find a unique name if sNewName already exists
+ OUString sFind(sNewName);
+ sal_Int32 nIndex = 0;
+ while(m_xDBContext->hasByName(sFind))
+ {
+ sFind = sNewName + OUString::number(++nIndex);
+ }
+ uno::Reference<XPropertySet> xDataProperties(xNewInstance, UNO_QUERY);
+
+ //only the 'path' has to be added
+ INetURLObject aTempURL(aURL);
+ aTempURL.removeSegment();
+ aTempURL.removeFinalSlash();
+ const OUString sDBURL("sdbc:flat:" + aTempURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ xDataProperties->setPropertyValue("URL", Any(sDBURL));
+ //set the filter to the file name without extension
+ uno::Sequence<OUString> aFilters { sNewName };
+ xDataProperties->setPropertyValue("TableFilter", Any(aFilters));
+
+ uno::Sequence<PropertyValue> aInfo
+ {
+ comphelper::makePropertyValue("FieldDelimiter", OUString('\t')),
+ comphelper::makePropertyValue("StringDelimiter", OUString('"')),
+ comphelper::makePropertyValue("Extension", aURL.getExtension()), //"csv
+ comphelper::makePropertyValue("CharSet", OUString("UTF-8"))
+ };
+ xDataProperties->setPropertyValue("Info", Any(aInfo));
+
+ uno::Reference<sdb::XDocumentDataSource> xDS(xNewInstance, UNO_QUERY_THROW);
+ uno::Reference<frame::XStorable> xStore(xDS->getDatabaseDocument(), UNO_QUERY_THROW);
+ OUString const sExt(".odb");
+ OUString sTmpName;
+ {
+ OUString sHomePath(SvtPathOptions().GetWorkPath());
+ utl::TempFile aTempFile(sFind, true, &sExt, &sHomePath);
+ aTempFile.EnableKillingFile();
+ sTmpName = aTempFile.GetURL();
+ }
+ xStore->storeAsURL(sTmpName, Sequence< PropertyValue >());
+
+ m_xDBContext->registerObject( sFind, xNewInstance );
+ //now insert the new source into the ListBox
+ m_xListLB->append(m_xIter.get());
+ m_xListLB->set_text(*m_xIter, sFind, 0);
+ m_xListLB->set_text(*m_xIter, aFilters[0], 1);
+ m_aUserData.emplace_back(new AddressUserData_Impl);
+ AddressUserData_Impl* pUserData = m_aUserData.back().get();
+ m_xListLB->set_id(*m_xIter, weld::toId(pUserData));
+ m_xListLB->select(*m_xIter);
+ ListBoxSelectHdl_Impl(*m_xListLB);
+ m_xCreateListPB->set_sensitive(false);
+ m_xRemovePB->set_sensitive(true);
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+IMPL_LINK_NOARG(SwAddressListDialog, EditHdl_Impl, weld::Button&, void)
+{
+ int nEntry = m_xListLB->get_selected_index();
+ AddressUserData_Impl* pUserData = nEntry != -1 ? weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nEntry)) : nullptr;
+ if (!pUserData || pUserData->sURL.isEmpty())
+ return;
+
+ if(pUserData->xResultSet.is())
+ {
+ SwMailMergeConfigItem& rConfigItem = m_pAddressPage->GetWizard()->GetConfigItem();
+ if(rConfigItem.GetResultSet() != pUserData->xResultSet)
+ ::comphelper::disposeComponent( pUserData->xResultSet );
+ pUserData->xResultSet = nullptr;
+
+ rConfigItem.DisposeResultSet();
+ }
+ pUserData->xSource.clear();
+ pUserData->xColumnsSupplier.clear();
+ pUserData->xConnection.clear();
+ // will automatically close if it was the las reference
+ SwCreateAddressListDialog aDlg(m_xDialog.get(), pUserData->sURL,
+ m_pAddressPage->GetWizard()->GetConfigItem());
+ aDlg.run();
+};
+
+IMPL_LINK_NOARG(SwAddressListDialog, ListBoxSelectHdl_Impl, weld::TreeView&, void)
+{
+ int nSelect = m_xListLB->get_selected_index();
+ Application::PostUserEvent( LINK( this, SwAddressListDialog,
+ StaticListBoxSelectHdl_Impl ), reinterpret_cast<void*>(nSelect) );
+}
+
+IMPL_LINK(SwAddressListDialog, StaticListBoxSelectHdl_Impl, void*, p, void)
+{
+ int nSelect = reinterpret_cast<sal_IntPtr>(p);
+ //prevent nested calls of the select handler
+ if (m_bInSelectHdl)
+ return;
+ weld::WaitObject aWait(m_xDialog.get());
+ m_bInSelectHdl = true;
+ AddressUserData_Impl* pUserData = nullptr;
+ if (nSelect != -1)
+ {
+ const OUString sTable(m_xListLB->get_text(nSelect, 1));
+ if (sTable.isEmpty())
+ {
+ m_xListLB->set_text(nSelect, m_sConnecting, 1);
+ }
+
+ pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ if(pUserData->nTableAndQueryCount > 1 || pUserData->nTableAndQueryCount == -1)
+ {
+ DetectTablesAndQueries(nSelect, sTable.isEmpty());
+ }
+ else
+ {
+ //otherwise set the selected db-data
+ m_aDBData.sDataSource = m_xListLB->get_text(nSelect, 0);
+ m_aDBData.sCommand = m_xListLB->get_text(nSelect, 1);
+ m_aDBData.nCommandType = pUserData->nCommandType;
+ m_xOK->set_sensitive(true);
+ }
+ if (m_xListLB->get_text(nSelect, 1) == m_sConnecting)
+ m_xListLB->set_text(nSelect, OUString(), 1);
+ }
+ m_xEditPB->set_sensitive(pUserData && !pUserData->sURL.isEmpty() &&
+ SWUnoHelper::UCB_IsFile( pUserData->sURL ) && //#i97577#
+ !SWUnoHelper::UCB_IsReadOnlyFileName( pUserData->sURL ) );
+ m_bInSelectHdl = false;
+}
+
+// detect the number of tables for a data source
+// if only one is available then set it at the entry
+void SwAddressListDialog::DetectTablesAndQueries(
+ int nSelect,
+ bool bWidthDialog)
+{
+ try
+ {
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ uno::Reference<XCompletedConnection> xComplConnection;
+ if(!pUserData->xConnection.is())
+ {
+ m_aDBData.sDataSource = m_xListLB->get_text(nSelect, 0);
+ m_xDBContext->getByName(m_aDBData.sDataSource) >>= xComplConnection;
+ pUserData->xSource.set(xComplConnection, UNO_QUERY);
+
+ uno::Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< XInteractionHandler > xHandler = InteractionHandler::createWithParent(xContext, nullptr);
+ pUserData->xConnection = SharedConnection( xComplConnection->connectWithCompletion( xHandler ) );
+ }
+ if(pUserData->xConnection.is())
+ {
+ sal_Int32 nTables = 0;
+ uno::Sequence<OUString> aTables;
+ uno::Sequence<OUString> aQueries;
+ uno::Reference<XTablesSupplier> xTSupplier(pUserData->xConnection, UNO_QUERY);
+ if(xTSupplier.is())
+ {
+ uno::Reference<XNameAccess> xTables = xTSupplier->getTables();
+ aTables = xTables->getElementNames();
+ nTables += aTables.getLength();
+ }
+ uno::Reference<XQueriesSupplier> xQSupplier(pUserData->xConnection, UNO_QUERY);
+ if(xQSupplier.is())
+ {
+ uno::Reference<XNameAccess> xQueries = xQSupplier->getQueries();
+ aQueries = xQueries->getElementNames();
+ nTables += aQueries.getLength();
+ }
+ pUserData->nTableAndQueryCount = nTables;
+ if(nTables > 1 && bWidthDialog)
+ {
+ //now call the table select dialog - if more than one table exists
+ SwSelectDBTableDialog aDlg(m_xDialog.get(), pUserData->xConnection);
+ const OUString sTable = m_xListLB->get_text(nSelect, 1);
+ if(!sTable.isEmpty())
+ aDlg.SetSelectedTable(sTable, pUserData->nCommandType == CommandType::TABLE);
+ if(RET_OK == aDlg.run())
+ {
+ bool bIsTable;
+ m_aDBData.sCommand = aDlg.GetSelectedTable(bIsTable);
+ m_aDBData.nCommandType = bIsTable ? CommandType::TABLE : CommandType::QUERY;
+ pUserData->nCommandType = m_aDBData.nCommandType;
+ }
+ }
+ else if(nTables == 1)
+ {
+ if(aTables.hasElements())
+ {
+ m_aDBData.sCommand = aTables[0];
+ m_aDBData.nCommandType = CommandType::TABLE;
+ }
+ else
+ {
+ m_aDBData.sCommand = aQueries[0];
+ m_aDBData.nCommandType = CommandType::QUERY;
+ }
+ }
+ }
+ if ( !m_aDBData.sCommand.isEmpty() )
+ {
+ uno::Reference<beans::XPropertySet> xSourceProperties;
+ m_xDBContext->getByName(m_aDBData.sDataSource) >>= xSourceProperties;
+ pUserData->sURL = lcl_getFlatURL( xSourceProperties );
+
+ pUserData->xColumnsSupplier = SwDBManager::GetColumnSupplier(pUserData->xConnection,
+ m_aDBData.sCommand,
+ m_aDBData.nCommandType == CommandType::TABLE ?
+ SwDBSelect::TABLE : SwDBSelect::QUERY );
+ //#i97577#
+ if( pUserData->xColumnsSupplier.is() )
+ m_xListLB->set_text(nSelect, m_aDBData.sCommand, 1);
+ else
+ m_xListLB->set_text(nSelect, OUString(), 1);
+ }
+ const OUString sCommand = m_xListLB->get_text(nSelect, 1);
+ m_xOK->set_sensitive(!sCommand.isEmpty());
+ m_xFilterPB->set_sensitive( pUserData->xConnection.is() && !sCommand.isEmpty() );
+ m_xTablePB->set_sensitive( pUserData->nTableAndQueryCount > 1 );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "exception caught in SwAddressListDialog::DetectTablesAndQueries");
+ m_xOK->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(SwAddressListDialog, TableSelectHdl_Impl, weld::Button&, rButton, void)
+{
+ TableSelectHdl(&rButton);
+}
+
+void SwAddressListDialog::TableSelectHdl(const weld::Button* pButton)
+{
+ weld::WaitObject aWait(m_xDialog.get());
+
+ int nSelect = m_xListLB->get_selected_index();
+ if (nSelect != -1)
+ {
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ //only call the table select dialog if tables have not been searched for or there
+ //are more than 1
+ const OUString sTable = m_xListLB->get_text(nSelect, 1);
+ if( pUserData->nTableAndQueryCount > 1 || pUserData->nTableAndQueryCount == -1)
+ {
+ DetectTablesAndQueries(nSelect, (pButton != nullptr) || sTable.isEmpty());
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SwAddressListDialog, OKHdl_Impl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+uno::Reference< XDataSource> SwAddressListDialog::GetSource() const
+{
+ uno::Reference< XDataSource> xRet;
+ int nSelect = m_xListLB->get_selected_index();
+ if (nSelect != -1)
+ {
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ xRet = pUserData->xSource;
+ }
+ return xRet;
+
+}
+
+SharedConnection SwAddressListDialog::GetConnection() const
+{
+ SharedConnection xRet;
+ int nSelect = m_xListLB->get_selected_index();
+ if (nSelect != -1)
+ {
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ xRet = pUserData->xConnection;
+ }
+ return xRet;
+}
+
+uno::Reference< XColumnsSupplier> SwAddressListDialog::GetColumnsSupplier() const
+{
+ uno::Reference< XColumnsSupplier> xRet;
+ int nSelect = m_xListLB->get_selected_index();
+ if (nSelect != -1)
+ {
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ xRet = pUserData->xColumnsSupplier;
+ }
+ return xRet;
+}
+
+OUString SwAddressListDialog::GetFilter() const
+{
+ int nSelect = m_xListLB->get_selected_index();
+ if (nSelect != -1)
+ {
+ AddressUserData_Impl* pUserData = weld::fromId<AddressUserData_Impl*>(m_xListLB->get_id(nSelect));
+ return pUserData->sFilter;
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/addresslistdialog.hxx b/sw/source/ui/dbui/addresslistdialog.hxx
new file mode 100644
index 000000000..dc0873af0
--- /dev/null
+++ b/sw/source/ui/dbui/addresslistdialog.hxx
@@ -0,0 +1,101 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_ADDRESSLISTDIALOG_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_ADDRESSLISTDIALOG_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <swdbdata.hxx>
+#include <sharedconnection.hxx>
+
+namespace com::sun::star{
+ namespace container{
+ class XNameAccess;
+ }
+ namespace sdb{
+ class XDatabaseContext;
+ }
+ namespace sdbc{
+ class XDataSource;
+ }
+ namespace sdbcx{
+ class XColumnsSupplier;
+ }
+}
+class SwMailMergeAddressBlockPage;
+
+struct AddressUserData_Impl;
+
+class SwAddressListDialog : public SfxDialogController
+{
+ OUString m_sConnecting;
+
+ bool m_bInSelectHdl;
+
+ SwMailMergeAddressBlockPage* m_pAddressPage;
+
+ css::uno::Reference< css::sdb::XDatabaseContext> m_xDBContext;
+
+ SwDBData m_aDBData;
+
+ std::vector<std::unique_ptr<AddressUserData_Impl>> m_aUserData;
+
+ std::unique_ptr<weld::Label> m_xDescriptionFI;
+ std::unique_ptr<weld::Label> m_xConnecting;
+ std::unique_ptr<weld::TreeView> m_xListLB;
+ std::unique_ptr<weld::Button> m_xLoadListPB;
+ std::unique_ptr<weld::Button> m_xRemovePB;
+ std::unique_ptr<weld::Button> m_xCreateListPB;
+ std::unique_ptr<weld::Button> m_xFilterPB;
+ std::unique_ptr<weld::Button> m_xEditPB;
+ std::unique_ptr<weld::Button> m_xTablePB;
+ std::unique_ptr<weld::Button> m_xOK;
+ std::unique_ptr<weld::TreeIter> m_xIter;
+
+ void DetectTablesAndQueries(int Select, bool bWidthDialog);
+
+ DECL_LINK(FilterHdl_Impl, weld::Button&, void);
+ DECL_LINK(LoadHdl_Impl, weld::Button&, void);
+ DECL_LINK(CreateHdl_Impl, weld::Button&, void);
+ DECL_LINK(RemoveHdl_Impl, weld::Button&, void);
+ DECL_LINK(ListBoxSelectHdl_Impl, weld::TreeView&, void);
+ DECL_LINK(EditHdl_Impl, weld::Button&, void);
+ DECL_LINK(TableSelectHdl_Impl, weld::Button&, void);
+ void TableSelectHdl(const weld::Button* pButton);
+ DECL_LINK(OKHdl_Impl, weld::Button&, void);
+
+ DECL_LINK(StaticListBoxSelectHdl_Impl, void*, void);
+
+public:
+ SwAddressListDialog(SwMailMergeAddressBlockPage* pParent);
+ virtual ~SwAddressListDialog() override;
+
+ css::uno::Reference< css::sdbc::XDataSource>
+ GetSource() const;
+
+ SharedConnection GetConnection() const;
+
+ css::uno::Reference< css::sdbcx::XColumnsSupplier>
+ GetColumnsSupplier() const;
+
+ const SwDBData& GetDBData() const {return m_aDBData;}
+ OUString GetFilter() const;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/createaddresslistdialog.cxx b/sw/source/ui/dbui/createaddresslistdialog.cxx
new file mode 100644
index 000000000..a3276c72c
--- /dev/null
+++ b/sw/source/ui/dbui/createaddresslistdialog.cxx
@@ -0,0 +1,589 @@
+/* -*- 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 <cstddef>
+
+#include <osl/diagnose.h>
+#include <swtypes.hxx>
+#include "createaddresslistdialog.hxx"
+#include "customizeaddresslistdialog.hxx"
+#include <mmconfigitem.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/docfile.hxx>
+#include <rtl/textenc.h>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <tools/urlobj.hxx>
+#include <o3tl/string_view.hxx>
+#include <strings.hrc>
+#include <map>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui::dialogs;
+
+namespace {
+
+struct SwAddressFragment
+{
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::Entry> m_xEntry;
+ weld::Container* m_pGrid;
+
+ SwAddressFragment(weld::Container* pGrid, int nLine)
+ : m_xBuilder(Application::CreateBuilder(pGrid, "modules/swriter/ui/addressfragment.ui"))
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xEntry(m_xBuilder->weld_entry("entry"))
+ , m_pGrid(pGrid)
+ {
+ m_xLabel->set_grid_left_attach(0);
+ m_xLabel->set_grid_top_attach(nLine);
+
+ m_xEntry->set_grid_left_attach(1);
+ m_xEntry->set_grid_top_attach(nLine);
+ }
+
+ ~SwAddressFragment()
+ {
+ m_pGrid->move(m_xEntry.get(), nullptr);
+ m_pGrid->move(m_xLabel.get(), nullptr);
+ }
+};
+
+}
+
+class SwAddressControl_Impl
+{
+ std::map<weld::Entry*, sal_Int32> m_aEditLines;
+
+ SwCSVData* m_pData;
+ sal_uInt32 m_nCurrentDataSet;
+
+ bool m_bNoDataSet;
+
+ std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
+ std::unique_ptr<weld::Container> m_xWindow;
+ std::vector<std::unique_ptr<SwAddressFragment>> m_aLines;
+
+ DECL_LINK(GotFocusHdl_Impl, weld::Widget&, void);
+ DECL_LINK(EditModifyHdl_Impl, weld::Entry&, void);
+
+ void MakeVisible(const tools::Rectangle& aRect);
+
+public:
+ SwAddressControl_Impl(weld::Builder& rBuilder);
+
+ void SetData(SwCSVData& rDBData);
+
+ void SetCurrentDataSet(sal_uInt32 nSet);
+ void CurrentDataSetInvalidated() { m_nCurrentDataSet = std::numeric_limits<sal_uInt32>::max(); }
+ sal_uInt32 GetCurrentDataSet() const { return m_nCurrentDataSet; }
+ void SetCursorTo(std::size_t nElement);
+};
+
+SwAddressControl_Impl::SwAddressControl_Impl(weld::Builder& rBuilder)
+ : m_pData(nullptr)
+ , m_nCurrentDataSet(0)
+ , m_bNoDataSet(true)
+ , m_xScrollBar(rBuilder.weld_scrolled_window("scrollwin"))
+ , m_xWindow(rBuilder.weld_container("CONTAINER"))
+{
+}
+
+void SwAddressControl_Impl::SetData(SwCSVData& rDBData)
+{
+ m_pData = &rDBData;
+ //when the address data is updated then remove the controls and build again
+ if (!m_aLines.empty())
+ {
+ m_aLines.clear();
+ m_bNoDataSet = true;
+ }
+
+ Link<weld::Widget&,void> aFocusLink = LINK(this, SwAddressControl_Impl, GotFocusHdl_Impl);
+ Link<weld::Entry&,void> aEditModifyLink = LINK(this, SwAddressControl_Impl, EditModifyHdl_Impl);
+ sal_Int32 nLines = 0;
+ for (const auto& rHeader : m_pData->aDBColumnHeaders)
+ {
+ m_aLines.emplace_back(new SwAddressFragment(m_xWindow.get(), nLines));
+
+ // when we have one line, measure it to get the line height to use as
+ // the basis for overall size request
+ if (nLines == 0)
+ {
+ auto nLineHeight = m_xWindow->get_preferred_size().Height();
+ m_xScrollBar->set_size_request(m_xScrollBar->get_approximate_digit_width() * 65,
+ nLineHeight * 10);
+ }
+
+ weld::Label* pNewFT = m_aLines.back()->m_xLabel.get();
+ weld::Entry* pNewED = m_aLines.back()->m_xEntry.get();
+ //set nLines a position identifier - used in the ModifyHdl
+ m_aEditLines[pNewED] = nLines;
+ pNewED->connect_focus_in(aFocusLink);
+ pNewED->connect_changed(aEditModifyLink);
+
+ pNewFT->set_label(rHeader);
+
+ nLines++;
+ }
+}
+
+void SwAddressControl_Impl::SetCurrentDataSet(sal_uInt32 nSet)
+{
+ if(!(m_bNoDataSet || m_nCurrentDataSet != nSet))
+ return;
+
+ m_bNoDataSet = false;
+ m_nCurrentDataSet = nSet;
+ OSL_ENSURE(m_pData->aDBData.size() > m_nCurrentDataSet, "wrong data set index");
+ if(m_pData->aDBData.size() > m_nCurrentDataSet)
+ {
+ sal_uInt32 nIndex = 0;
+ for(auto& rLine : m_aLines)
+ {
+ OSL_ENSURE(nIndex < m_pData->aDBData[m_nCurrentDataSet].size(),
+ "number of columns doesn't match number of Edits");
+ rLine->m_xEntry->set_text(m_pData->aDBData[m_nCurrentDataSet][nIndex]);
+ ++nIndex;
+ }
+ }
+}
+
+IMPL_LINK(SwAddressControl_Impl, GotFocusHdl_Impl, weld::Widget&, rEdit, void)
+{
+ int x, y, width, height;
+ rEdit.get_extents_relative_to(*m_xWindow, x, y, width, height);
+ // the container has a border of 3 in the .ui
+ tools::Rectangle aRect(Point(x - 3, y - 3), Size(width + 6, height + 6));
+ MakeVisible(aRect);
+}
+
+void SwAddressControl_Impl::MakeVisible(const tools::Rectangle & rRect)
+{
+ //determine range of visible positions
+ auto nMinVisiblePos = m_xScrollBar->vadjustment_get_value();
+ auto nMaxVisiblePos = nMinVisiblePos + m_xScrollBar->vadjustment_get_page_size();
+ if (rRect.Top() < nMinVisiblePos || rRect.Bottom() > nMaxVisiblePos)
+ m_xScrollBar->vadjustment_set_value(rRect.Top());
+}
+
+// copy data changes into database
+IMPL_LINK(SwAddressControl_Impl, EditModifyHdl_Impl, weld::Entry&, rEdit, void)
+{
+ //get the data element number of the current set
+ sal_Int32 nIndex = m_aEditLines[&rEdit];
+ //get the index of the set
+ OSL_ENSURE(m_pData->aDBData.size() > m_nCurrentDataSet, "wrong data set index" );
+ if (m_pData->aDBData.size() > m_nCurrentDataSet)
+ {
+ m_pData->aDBData[m_nCurrentDataSet][nIndex] = rEdit.get_text();
+ }
+}
+
+void SwAddressControl_Impl::SetCursorTo(std::size_t nElement)
+{
+ if (nElement < m_aLines.size())
+ {
+ weld::Entry* pEdit = m_aLines[nElement]->m_xEntry.get();
+ pEdit->grab_focus();
+ }
+
+}
+
+SwCreateAddressListDialog::SwCreateAddressListDialog(
+ weld::Window* pParent, const OUString& rURL, SwMailMergeConfigItem const & rConfig)
+ : SfxDialogController(pParent, "modules/swriter/ui/createaddresslist.ui", "CreateAddressList")
+ , m_sAddressListFilterName(SwResId(ST_FILTERNAME))
+ , m_sURL(rURL)
+ , m_pCSVData(new SwCSVData)
+ , m_xAddressControl(new SwAddressControl_Impl(*m_xBuilder))
+ , m_xNewPB(m_xBuilder->weld_button("NEW"))
+ , m_xDeletePB(m_xBuilder->weld_button("DELETE"))
+ , m_xFindPB(m_xBuilder->weld_button("FIND"))
+ , m_xCustomizePB(m_xBuilder->weld_button("CUSTOMIZE"))
+ , m_xStartPB(m_xBuilder->weld_button("START"))
+ , m_xPrevPB(m_xBuilder->weld_button("PREV"))
+ , m_xSetNoED(m_xBuilder->weld_entry("SETNOED"))
+ , m_xSetNoNF(m_xBuilder->weld_spin_button("SETNOSB"))
+ , m_xNextPB(m_xBuilder->weld_button("NEXT"))
+ , m_xEndPB(m_xBuilder->weld_button("END"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+{
+ m_xSetNoNF->set_min(1);
+
+ m_xNewPB->connect_clicked(LINK(this, SwCreateAddressListDialog, NewHdl_Impl));
+ m_xDeletePB->connect_clicked(LINK(this, SwCreateAddressListDialog, DeleteHdl_Impl));
+ m_xFindPB->connect_clicked(LINK(this, SwCreateAddressListDialog, FindHdl_Impl));
+ m_xCustomizePB->connect_clicked(LINK(this, SwCreateAddressListDialog, CustomizeHdl_Impl));
+ m_xOK->connect_clicked(LINK(this, SwCreateAddressListDialog, OkHdl_Impl));
+
+ Link<weld::Button&,void> aLk = LINK(this, SwCreateAddressListDialog, DBCursorHdl_Impl);
+ m_xStartPB->connect_clicked(aLk);
+ m_xPrevPB->connect_clicked(aLk);
+ m_xSetNoED->connect_changed(LINK(this, SwCreateAddressListDialog, DBNumCursorHdl_Impl));
+ m_xSetNoED->connect_focus_out(LINK(this, SwCreateAddressListDialog, RefreshNum_Impl));
+ m_xNextPB->connect_clicked(aLk);
+ m_xEndPB->connect_clicked(aLk);
+
+ if (!m_sURL.isEmpty())
+ {
+ //file exists, has to be loaded here
+ SfxMedium aMedium( m_sURL, StreamMode::READ );
+ SvStream* pStream = aMedium.GetInStream();
+ if(pStream)
+ {
+ pStream->SetLineDelimiter( LINEEND_LF );
+ pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
+
+ OUString sLine;
+ bool bRead = pStream->ReadByteStringLine( sLine, RTL_TEXTENCODING_UTF8 );
+
+ if(bRead && !sLine.isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ const std::u16string_view sHeader = o3tl::getToken(sLine, 0, '\t', nIndex );
+ OSL_ENSURE(sHeader.size() > 2 &&
+ o3tl::starts_with(sHeader, u"\"") && o3tl::ends_with(sHeader, u"\""),
+ "Wrong format of header");
+ if(sHeader.size() > 2)
+ {
+ m_pCSVData->aDBColumnHeaders.push_back( OUString(sHeader.substr(1, sHeader.size() -2)));
+ }
+ }
+ while (nIndex > 0);
+ }
+ while(pStream->ReadByteStringLine( sLine, RTL_TEXTENCODING_UTF8 ))
+ {
+ std::vector<OUString> aNewData;
+ //analyze data line
+ sal_Int32 nIndex = { sLine.isEmpty() ? -1 : 0 };
+ while (nIndex >= 0)
+ {
+ const OUString sData = sLine.getToken( 0, '\t', nIndex );
+ OSL_ENSURE( sData.startsWith("\"") && sData.endsWith("\""),
+ "Wrong format of line");
+ if(sData.getLength() >= 2)
+ aNewData.push_back(sData.copy(1, sData.getLength() - 2));
+ else
+ aNewData.push_back(sData);
+ }
+ m_pCSVData->aDBData.push_back( aNewData );
+ }
+ }
+ }
+ else
+ {
+ //database has to be created
+ const std::vector<std::pair<OUString, int>>& rAddressHeader = rConfig.GetDefaultAddressHeaders();
+ const sal_uInt32 nCount = rAddressHeader.size();
+ for(sal_uInt32 nHeader = 0; nHeader < nCount; ++nHeader)
+ m_pCSVData->aDBColumnHeaders.push_back(rAddressHeader[nHeader].first);
+ std::vector<OUString> aNewData;
+ aNewData.insert(aNewData.begin(), nCount, OUString());
+ m_pCSVData->aDBData.push_back(aNewData);
+ }
+ //now fill the address control
+ m_xAddressControl->SetData(*m_pCSVData);
+ m_xAddressControl->SetCurrentDataSet(0);
+ m_xSetNoNF->set_max(m_pCSVData->aDBData.size());
+
+ m_xSetNoNF->set_value(1);
+ RefreshNum_Impl(*m_xSetNoED);
+
+ UpdateButtons();
+}
+
+SwCreateAddressListDialog::~SwCreateAddressListDialog()
+{
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, NewHdl_Impl, weld::Button&, void)
+{
+ sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet();
+ std::vector<OUString> aNewData;
+ aNewData.insert(aNewData.begin(), m_pCSVData->aDBColumnHeaders.size(), OUString());
+ m_pCSVData->aDBData.insert(m_pCSVData->aDBData.begin() + ++nCurrent, aNewData);
+ m_xSetNoNF->set_max(m_pCSVData->aDBData.size());
+ //the NumericField start at 1
+ m_xSetNoNF->set_value(nCurrent + 1);
+ RefreshNum_Impl(*m_xSetNoED);
+ //the address control starts at 0
+ m_xAddressControl->SetCurrentDataSet(nCurrent);
+ UpdateButtons();
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, DeleteHdl_Impl, weld::Button&, void)
+{
+ sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet();
+ if (m_pCSVData->aDBData.size() > 1)
+ {
+ m_pCSVData->aDBData.erase(m_pCSVData->aDBData.begin() + nCurrent);
+ if (nCurrent)
+ --nCurrent;
+ }
+ else
+ {
+ // if only one set is available then clear the data
+ m_pCSVData->aDBData[0].assign(m_pCSVData->aDBData[0].size(), OUString());
+ m_xDeletePB->set_sensitive(false);
+ }
+ m_xAddressControl->CurrentDataSetInvalidated();
+ m_xAddressControl->SetCurrentDataSet(nCurrent);
+ m_xSetNoNF->set_max(m_pCSVData->aDBData.size());
+ UpdateButtons();
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, FindHdl_Impl, weld::Button&, void)
+{
+ if (!m_xFindDlg)
+ {
+ m_xFindDlg.reset(new SwFindEntryDialog(this));
+ weld::ComboBox& rColumnBox = m_xFindDlg->GetFieldsListBox();
+ for(const auto& rHeader : m_pCSVData->aDBColumnHeaders)
+ rColumnBox.append_text(rHeader);
+ rColumnBox.set_active(0);
+ m_xFindDlg->show();
+ }
+ else
+ m_xFindDlg->set_visible(!m_xFindDlg->get_visible());
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, CustomizeHdl_Impl, weld::Button&, void)
+{
+ SwCustomizeAddressListDialog aDlg(m_xDialog.get(), *m_pCSVData);
+ if (aDlg.run() == RET_OK)
+ {
+ m_pCSVData = aDlg.ReleaseNewData();
+ m_xAddressControl->SetData(*m_pCSVData);
+ m_xAddressControl->SetCurrentDataSet(m_xAddressControl->GetCurrentDataSet());
+ }
+
+ //update find dialog
+ if (m_xFindDlg)
+ {
+ weld::ComboBox& rColumnBox = m_xFindDlg->GetFieldsListBox();
+ rColumnBox.clear();
+ for(const auto& rHeader : m_pCSVData->aDBColumnHeaders)
+ rColumnBox.append_text(rHeader);
+ }
+}
+
+namespace
+{
+
+void lcl_WriteValues(const std::vector<OUString> *pFields, SvStream* pStream)
+{
+ OUStringBuffer sLine;
+ const std::vector< OUString >::const_iterator aBegin = pFields->begin();
+ const std::vector< OUString >::const_iterator aEnd = pFields->end();
+ for(std::vector< OUString >::const_iterator aIter = aBegin; aIter != aEnd; ++aIter)
+ {
+ if (aIter==aBegin)
+ {
+ sLine.append("\"" + *aIter + "\"");
+ }
+ else
+ {
+ sLine.append("\t\"" + *aIter + "\"");
+ }
+ }
+ pStream->WriteByteStringLine( sLine.makeStringAndClear(), RTL_TEXTENCODING_UTF8 );
+}
+
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, OkHdl_Impl, weld::Button&, void)
+{
+ if(m_sURL.isEmpty())
+ {
+ sfx2::FileDialogHelper aDlgHelper(TemplateDescription::FILESAVE_SIMPLE,
+ FileDialogFlags::NONE, m_xDialog.get());
+ aDlgHelper.SetContext(sfx2::FileDialogHelper::WriterCreateAddressList);
+ uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
+ xFP->appendFilter( m_sAddressListFilterName, "*.csv" );
+ xFP->setCurrentFilter( m_sAddressListFilterName ) ;
+
+ if( ERRCODE_NONE == aDlgHelper.Execute() )
+ {
+ m_sURL = xFP->getSelectedFiles().getConstArray()[0];
+ INetURLObject aResult( m_sURL );
+ aResult.setExtension(u"csv");
+ m_sURL = aResult.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+ }
+ if(m_sURL.isEmpty())
+ return;
+
+ SfxMedium aMedium( m_sURL, StreamMode::READWRITE|StreamMode::TRUNC );
+ SvStream* pStream = aMedium.GetOutStream();
+ pStream->SetLineDelimiter( LINEEND_LF );
+ pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
+
+ lcl_WriteValues(&(m_pCSVData->aDBColumnHeaders), pStream);
+
+ for(const auto& rData : m_pCSVData->aDBData)
+ {
+ lcl_WriteValues(&rData, pStream);
+ }
+ aMedium.Commit();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(SwCreateAddressListDialog, DBCursorHdl_Impl, weld::Button&, rButton, void)
+{
+ int nValue = m_xSetNoNF->get_value();
+
+ if (&rButton == m_xStartPB.get())
+ nValue = 1;
+ else if (&rButton == m_xPrevPB.get())
+ {
+ if (nValue > 1)
+ --nValue;
+ }
+ else if (&rButton == m_xNextPB.get())
+ {
+ if (nValue < m_xSetNoNF->get_max())
+ ++nValue;
+ }
+ else //m_aEndPB
+ nValue = m_xSetNoNF->get_max();
+ if (nValue != m_xSetNoNF->get_value())
+ {
+ m_xSetNoNF->set_value(nValue);
+ RefreshNum_Impl(*m_xSetNoED);
+ DBNumCursor();
+ }
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, DBNumCursorHdl_Impl, weld::Entry&, void)
+{
+ m_xSetNoNF->set_text(m_xSetNoED->get_text());
+ DBNumCursor();
+}
+
+IMPL_LINK_NOARG(SwCreateAddressListDialog, RefreshNum_Impl, weld::Widget&, void)
+{
+ m_xSetNoED->set_text(OUString::number(m_xSetNoNF->get_value()));
+}
+
+void SwCreateAddressListDialog::DBNumCursor()
+{
+ m_xAddressControl->SetCurrentDataSet(m_xSetNoNF->get_value() - 1);
+ UpdateButtons();
+}
+
+void SwCreateAddressListDialog::UpdateButtons()
+{
+ sal_uInt32 nCurrent = static_cast< sal_uInt32 >(m_xSetNoNF->get_value() );
+ sal_uInt32 nSize = static_cast<sal_uInt32>(m_pCSVData->aDBData.size());
+ m_xStartPB->set_sensitive(nCurrent != 1);
+ m_xPrevPB->set_sensitive(nCurrent != 1);
+ m_xNextPB->set_sensitive(nCurrent != nSize);
+ m_xEndPB->set_sensitive(nCurrent != nSize);
+ m_xDeletePB->set_sensitive(nSize > 0);
+}
+
+void SwCreateAddressListDialog::Find(const OUString& rSearch, sal_Int32 nColumn)
+{
+ const OUString sSearch = rSearch.toAsciiLowerCase();
+ sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet();
+ //search forward
+ bool bFound = false;
+ sal_uInt32 nStart = nCurrent + 1;
+ sal_uInt32 nEnd = m_pCSVData->aDBData.size();
+ std::size_t nElement = 0;
+ sal_uInt32 nPos = 0;
+ for(short nTemp = 0; nTemp < 2 && !bFound; nTemp++)
+ {
+ for(nPos = nStart; nPos < nEnd; ++nPos)
+ {
+ std::vector< OUString> const & aData = m_pCSVData->aDBData[nPos];
+ if(nColumn >=0)
+ bFound = -1 != aData[static_cast<sal_uInt32>(nColumn)].toAsciiLowerCase().indexOf(sSearch);
+ else
+ {
+ for( nElement = 0; nElement < aData.size(); ++nElement)
+ {
+ bFound = -1 != aData[nElement].toAsciiLowerCase().indexOf(sSearch);
+ if(bFound)
+ {
+ nColumn = nElement; //TODO: std::size_t -> sal_Int32!
+ break;
+ }
+ }
+ }
+ if(bFound)
+ break;
+ }
+ nStart = 0;
+ nEnd = nCurrent + 1;
+ }
+ if(bFound)
+ {
+ m_xAddressControl->SetCurrentDataSet(nPos);
+ m_xSetNoNF->set_value( nPos + 1 );
+ RefreshNum_Impl(*m_xSetNoED);
+ UpdateButtons();
+ m_xAddressControl->SetCursorTo(nElement);
+ }
+}
+
+SwFindEntryDialog::SwFindEntryDialog(SwCreateAddressListDialog* pParent)
+ : GenericDialogController(pParent->getDialog(), "modules/swriter/ui/findentrydialog.ui", "FindEntryDialog")
+ , m_pParent(pParent)
+ , m_xFindED(m_xBuilder->weld_entry("entry"))
+ , m_xFindOnlyCB(m_xBuilder->weld_check_button("findin"))
+ , m_xFindOnlyLB(m_xBuilder->weld_combo_box("area"))
+ , m_xFindPB(m_xBuilder->weld_button("find"))
+ , m_xCancel(m_xBuilder->weld_button("cancel"))
+{
+ m_xFindPB->connect_clicked(LINK(this, SwFindEntryDialog, FindHdl_Impl));
+ m_xFindED->connect_changed(LINK(this, SwFindEntryDialog, FindEnableHdl_Impl));
+ m_xCancel->connect_clicked(LINK(this, SwFindEntryDialog, CloseHdl_Impl));
+}
+
+SwFindEntryDialog::~SwFindEntryDialog()
+{
+}
+
+IMPL_LINK_NOARG(SwFindEntryDialog, FindHdl_Impl, weld::Button&, void)
+{
+ sal_Int32 nColumn = -1;
+ if (m_xFindOnlyCB->get_active())
+ nColumn = m_xFindOnlyLB->get_active();
+ m_pParent->Find(m_xFindED->get_text(), nColumn);
+}
+
+IMPL_LINK_NOARG(SwFindEntryDialog, FindEnableHdl_Impl, weld::Entry&, void)
+{
+ m_xFindPB->set_sensitive(!m_xFindED->get_text().isEmpty());
+}
+
+IMPL_LINK_NOARG(SwFindEntryDialog, CloseHdl_Impl, weld::Button&, void)
+{
+ m_xDialog->hide();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/createaddresslistdialog.hxx b/sw/source/ui/dbui/createaddresslistdialog.hxx
new file mode 100644
index 000000000..d4f3e030b
--- /dev/null
+++ b/sw/source/ui/dbui/createaddresslistdialog.hxx
@@ -0,0 +1,108 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_CREATEADDRESSLISTDIALOG_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_CREATEADDRESSLISTDIALOG_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <vcl/weld.hxx>
+#include <vector>
+
+class SwAddressControl_Impl;
+class SwMailMergeConfigItem;
+
+// container of the created database
+struct SwCSVData
+{
+ std::vector< OUString > aDBColumnHeaders;
+ std::vector< std::vector< OUString> > aDBData;
+};
+
+class SwFindEntryDialog;
+class SwCreateAddressListDialog : public SfxDialogController
+{
+ OUString m_sAddressListFilterName;
+ OUString m_sURL;
+
+ std::unique_ptr<SwCSVData> m_pCSVData;
+ std::unique_ptr<SwFindEntryDialog> m_xFindDlg;
+
+ std::unique_ptr<SwAddressControl_Impl> m_xAddressControl;
+ std::unique_ptr<weld::Button> m_xNewPB;
+ std::unique_ptr<weld::Button> m_xDeletePB;
+ std::unique_ptr<weld::Button> m_xFindPB;
+ std::unique_ptr<weld::Button> m_xCustomizePB;
+ std::unique_ptr<weld::Button> m_xStartPB;
+ std::unique_ptr<weld::Button> m_xPrevPB;
+ std::unique_ptr<weld::Entry> m_xSetNoED;
+ std::unique_ptr<weld::SpinButton> m_xSetNoNF;
+ std::unique_ptr<weld::Button> m_xNextPB;
+ std::unique_ptr<weld::Button> m_xEndPB;
+ std::unique_ptr<weld::Button> m_xOK;
+
+ DECL_LINK(NewHdl_Impl, weld::Button&, void);
+ DECL_LINK(DeleteHdl_Impl, weld::Button&, void);
+ DECL_LINK(FindHdl_Impl, weld::Button&, void);
+ DECL_LINK(CustomizeHdl_Impl, weld::Button&, void);
+ DECL_LINK(OkHdl_Impl, weld::Button&, void);
+ DECL_LINK(DBCursorHdl_Impl, weld::Button&, void);
+ DECL_LINK(DBNumCursorHdl_Impl, weld::Entry&, void);
+ DECL_LINK(RefreshNum_Impl, weld::Widget&, void);
+ void DBNumCursor();
+
+ void UpdateButtons();
+
+public:
+ SwCreateAddressListDialog(weld::Window* pParent, const OUString& rURL, SwMailMergeConfigItem const & rConfig);
+ virtual ~SwCreateAddressListDialog() override;
+
+ const OUString& GetURL() const { return m_sURL; }
+ void Find( const OUString& rSearch, sal_Int32 nColumn);
+};
+
+class SwFindEntryDialog : public weld::GenericDialogController
+{
+ SwCreateAddressListDialog* m_pParent;
+
+ std::unique_ptr<weld::Entry> m_xFindED;
+ std::unique_ptr<weld::CheckButton> m_xFindOnlyCB;
+ std::unique_ptr<weld::ComboBox> m_xFindOnlyLB;
+ std::unique_ptr<weld::Button> m_xFindPB;
+ std::unique_ptr<weld::Button> m_xCancel;
+
+ DECL_LINK(FindHdl_Impl, weld::Button&, void);
+ DECL_LINK(FindEnableHdl_Impl, weld::Entry&, void);
+ DECL_LINK(CloseHdl_Impl, weld::Button&, void);
+
+public:
+ SwFindEntryDialog(SwCreateAddressListDialog* pParent);
+ virtual ~SwFindEntryDialog() override;
+
+ void show() { m_xDialog->show(); }
+ void set_visible(bool bVisible) { m_xDialog->set_visible(bVisible); }
+ bool get_visible() const { return m_xDialog->get_visible(); }
+
+ weld::ComboBox& GetFieldsListBox()
+ {
+ return *m_xFindOnlyLB;
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/customizeaddresslistdialog.cxx b/sw/source/ui/dbui/customizeaddresslistdialog.cxx
new file mode 100644
index 000000000..d5b9ffd68
--- /dev/null
+++ b/sw/source/ui/dbui/customizeaddresslistdialog.cxx
@@ -0,0 +1,181 @@
+/* -*- 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 "customizeaddresslistdialog.hxx"
+#include "createaddresslistdialog.hxx"
+
+SwCustomizeAddressListDialog::SwCustomizeAddressListDialog(
+ weld::Window* pParent, const SwCSVData& rOldData)
+ : SfxDialogController(pParent, "modules/swriter/ui/customizeaddrlistdialog.ui",
+ "CustomizeAddrListDialog")
+ , m_xNewData(new SwCSVData(rOldData))
+ , m_xFieldsLB(m_xBuilder->weld_tree_view("treeview"))
+ , m_xAddPB(m_xBuilder->weld_button("add"))
+ , m_xDeletePB(m_xBuilder->weld_button("delete"))
+ , m_xRenamePB(m_xBuilder->weld_button("rename"))
+ , m_xUpPB(m_xBuilder->weld_button("up"))
+ , m_xDownPB(m_xBuilder->weld_button("down"))
+{
+ m_xFieldsLB->set_size_request(-1, m_xFieldsLB->get_height_rows(14));
+
+ m_xFieldsLB->connect_changed(LINK(this, SwCustomizeAddressListDialog, ListBoxSelectHdl_Impl));
+ Link<weld::Button&,void> aAddRenameLk = LINK(this, SwCustomizeAddressListDialog, AddRenameHdl_Impl );
+ m_xAddPB->connect_clicked(aAddRenameLk);
+ m_xRenamePB->connect_clicked(aAddRenameLk);
+ m_xDeletePB->connect_clicked(LINK(this, SwCustomizeAddressListDialog, DeleteHdl_Impl ));
+ Link<weld::Button&,void> aUpDownLk = LINK(this, SwCustomizeAddressListDialog, UpDownHdl_Impl);
+ m_xUpPB->connect_clicked(aUpDownLk);
+ m_xDownPB->connect_clicked(aUpDownLk);
+
+ for (const auto& rHeader : m_xNewData->aDBColumnHeaders)
+ m_xFieldsLB->append_text(rHeader);
+
+ m_xFieldsLB->select(0);
+ UpdateButtons();
+}
+
+SwCustomizeAddressListDialog::~SwCustomizeAddressListDialog()
+{
+}
+
+IMPL_LINK_NOARG(SwCustomizeAddressListDialog, ListBoxSelectHdl_Impl, weld::TreeView&, void)
+{
+ UpdateButtons();
+}
+
+IMPL_LINK(SwCustomizeAddressListDialog, AddRenameHdl_Impl, weld::Button&, rButton, void)
+{
+ bool bRename = &rButton == m_xRenamePB.get();
+ auto nPos = m_xFieldsLB->get_selected_index();
+ if (nPos == -1)
+ nPos = 0;
+
+ std::unique_ptr<SwAddRenameEntryDialog> xDlg;
+ if (bRename)
+ xDlg.reset(new SwRenameEntryDialog(m_xDialog.get(), m_xNewData->aDBColumnHeaders));
+ else
+ xDlg.reset(new SwAddEntryDialog(m_xDialog.get(), m_xNewData->aDBColumnHeaders));
+ if (bRename)
+ {
+ OUString aTemp = m_xFieldsLB->get_text(nPos);
+ xDlg->SetFieldName(aTemp);
+ }
+ if (xDlg->run() == RET_OK)
+ {
+ OUString sNew = xDlg->GetFieldName();
+ if(bRename)
+ {
+ m_xNewData->aDBColumnHeaders[nPos] = sNew;
+ m_xFieldsLB->remove(nPos);
+ }
+ else
+ {
+ if (m_xFieldsLB->get_selected_index() != -1)
+ ++nPos; // append the new entry behind the selected
+ //add the new column
+ m_xNewData->aDBColumnHeaders.insert(m_xNewData->aDBColumnHeaders.begin() + nPos, sNew);
+ //add a new entry into all data arrays
+ for (auto& rData : m_xNewData->aDBData)
+ rData.insert(rData.begin() + nPos, OUString());
+
+ }
+
+ m_xFieldsLB->insert_text(nPos, sNew);
+ m_xFieldsLB->select(nPos);
+ }
+ UpdateButtons();
+}
+
+IMPL_LINK_NOARG(SwCustomizeAddressListDialog, DeleteHdl_Impl, weld::Button&, void)
+{
+ auto nPos = m_xFieldsLB->get_selected_index();
+ m_xFieldsLB->remove(nPos);
+ m_xFieldsLB->select(nPos > m_xFieldsLB->n_children() - 1 ? nPos - 1 : nPos);
+
+ //remove the column
+ m_xNewData->aDBColumnHeaders.erase(m_xNewData->aDBColumnHeaders.begin() + nPos);
+ //remove the data
+ for (auto& rData : m_xNewData->aDBData)
+ rData.erase(rData.begin() + nPos);
+
+ UpdateButtons();
+}
+
+IMPL_LINK(SwCustomizeAddressListDialog, UpDownHdl_Impl, weld::Button&, rButton, void)
+{
+ auto nPos = m_xFieldsLB->get_selected_index();
+ auto nOldPos = nPos;
+ OUString aTemp = m_xFieldsLB->get_text(nPos);
+ m_xFieldsLB->remove(nPos);
+ if (&rButton == m_xUpPB.get())
+ --nPos;
+ else
+ ++nPos;
+ m_xFieldsLB->insert_text(nPos, aTemp);
+ m_xFieldsLB->select(nPos);
+ //align m_xNewData
+ OUString sHeader = m_xNewData->aDBColumnHeaders[nOldPos];
+ m_xNewData->aDBColumnHeaders.erase(m_xNewData->aDBColumnHeaders.begin() + nOldPos);
+ m_xNewData->aDBColumnHeaders.insert(m_xNewData->aDBColumnHeaders.begin() + nPos, sHeader);
+ for (auto& rData : m_xNewData->aDBData)
+ {
+ OUString sData = rData[nOldPos];
+ rData.erase(rData.begin() + nOldPos);
+ rData.insert(rData.begin() + nPos, sData);
+ }
+
+ UpdateButtons();
+}
+
+void SwCustomizeAddressListDialog::UpdateButtons()
+{
+ auto nPos = m_xFieldsLB->get_selected_index();
+ auto nEntries = m_xFieldsLB->n_children();
+ m_xUpPB->set_sensitive(nPos > 0 && nEntries > 0);
+ m_xDownPB->set_sensitive(nPos < nEntries -1);
+ m_xDeletePB->set_sensitive(nEntries > 0);
+ m_xRenamePB->set_sensitive(nEntries > 0);
+}
+
+SwAddRenameEntryDialog::SwAddRenameEntryDialog(
+ weld::Window* pParent, const OUString& rUIXMLDescription, const OString& rID,
+ const std::vector< OUString >& rCSVHeader)
+ : SfxDialogController(pParent, rUIXMLDescription, rID)
+ , m_rCSVHeader(rCSVHeader)
+ , m_xFieldNameED(m_xBuilder->weld_entry("entry"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+{
+ m_xFieldNameED->connect_changed(LINK(this, SwAddRenameEntryDialog, ModifyHdl_Impl));
+ ModifyHdl_Impl(*m_xFieldNameED);
+}
+
+IMPL_LINK(SwAddRenameEntryDialog, ModifyHdl_Impl, weld::Entry&, rEdit, void)
+{
+ OUString sEntry = rEdit.get_text();
+ bool bFound = sEntry.isEmpty();
+
+ if(!bFound)
+ {
+ bFound = std::any_of(m_rCSVHeader.begin(), m_rCSVHeader.end(),
+ [&sEntry](const OUString& rHeader) { return rHeader == sEntry; });
+ }
+ m_xOK->set_sensitive(!bFound);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/customizeaddresslistdialog.hxx b/sw/source/ui/dbui/customizeaddresslistdialog.hxx
new file mode 100644
index 000000000..107d7b855
--- /dev/null
+++ b/sw/source/ui/dbui/customizeaddresslistdialog.hxx
@@ -0,0 +1,91 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_CUSTOMIZEADDRESSLISTDIALOG_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_CUSTOMIZEADDRESSLISTDIALOG_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <vcl/weld.hxx>
+
+#include "createaddresslistdialog.hxx"
+
+struct SwCSVData;
+
+class SwCustomizeAddressListDialog : public SfxDialogController
+{
+ std::unique_ptr<SwCSVData> m_xNewData;
+ std::unique_ptr<weld::TreeView> m_xFieldsLB;
+ std::unique_ptr<weld::Button> m_xAddPB;
+ std::unique_ptr<weld::Button> m_xDeletePB;
+ std::unique_ptr<weld::Button> m_xRenamePB;
+ std::unique_ptr<weld::Button> m_xUpPB;
+ std::unique_ptr<weld::Button> m_xDownPB;
+
+ DECL_LINK(AddRenameHdl_Impl, weld::Button&, void);
+ DECL_LINK(DeleteHdl_Impl, weld::Button&, void);
+ DECL_LINK(UpDownHdl_Impl, weld::Button&, void);
+ DECL_LINK(ListBoxSelectHdl_Impl, weld::TreeView&, void);
+
+ void UpdateButtons();
+public:
+ SwCustomizeAddressListDialog(weld::Window* pParent, const SwCSVData& rOldData);
+ virtual ~SwCustomizeAddressListDialog() override;
+
+ std::unique_ptr<SwCSVData> ReleaseNewData() { return std::move(m_xNewData);}
+};
+
+class SwAddRenameEntryDialog : public SfxDialogController
+{
+ const std::vector< OUString >& m_rCSVHeader;
+ std::unique_ptr<weld::Entry> m_xFieldNameED;
+ std::unique_ptr<weld::Button> m_xOK;
+
+ DECL_LINK(ModifyHdl_Impl, weld::Entry&, void);
+protected:
+ SwAddRenameEntryDialog(weld::Window* pParent, const OUString& rUIXMLDescription,
+ const OString& rID, const std::vector< OUString >& rCSVHeader);
+
+public:
+ void SetFieldName(const OUString& rName) { m_xFieldNameED->set_text(rName); }
+ OUString GetFieldName() const { return m_xFieldNameED->get_text(); }
+
+};
+
+class SwAddEntryDialog : public SwAddRenameEntryDialog
+{
+public:
+ SwAddEntryDialog(weld::Window* pParent, const std::vector< OUString >& rCSVHeader)
+ : SwAddRenameEntryDialog(pParent, "modules/swriter/ui/addentrydialog.ui",
+ "AddEntryDialog", rCSVHeader)
+ {
+ }
+};
+
+class SwRenameEntryDialog : public SwAddRenameEntryDialog
+{
+public:
+ SwRenameEntryDialog(weld::Window* pParent, const std::vector< OUString >& rCSVHeader)
+ : SwAddRenameEntryDialog(pParent, "modules/swriter/ui/renameentrydialog.ui",
+ "RenameEntryDialog", rCSVHeader)
+ {
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/dbinsdlg.cxx b/sw/source/ui/dbui/dbinsdlg.cxx
new file mode 100644
index 000000000..82839c6bf
--- /dev/null
+++ b/sw/source/ui/dbui/dbinsdlg.cxx
@@ -0,0 +1,1748 @@
+/* -*- 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 <dbinsdlg.hxx>
+
+#include <float.h>
+
+#include <hintids.hxx>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.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/beans/XPropertySet.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <svl/numuno.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/mnemonic.hxx>
+#include <svl/style.hxx>
+#include <svl/zformat.hxx>
+#include <sfx2/htmlmode.hxx>
+#include <svl/itemset.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <fmtclds.hxx>
+#include <tabcol.hxx>
+#include <uiitems.hxx>
+#include <viewopt.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <docsh.hxx>
+#include <dbmgr.hxx>
+#include <tblafmt.hxx>
+#include <cellatr.hxx>
+#include <swtablerep.hxx>
+#include <dbfld.hxx>
+#include <fmtcol.hxx>
+#include <swwait.hxx>
+#include <modcfg.hxx>
+#include <swmodule.hxx>
+#include <poolfmt.hxx>
+#include <connectivity/dbtools.hxx>
+
+#include <cmdid.h>
+#include <SwStyleNameMapper.hxx>
+#include <tabsh.hxx>
+#include <swabstdlg.hxx>
+#include <strings.hrc>
+#include <IDocumentMarkAccess.hxx>
+
+#include <o3tl/any.hxx>
+
+#include <memory>
+#include <string_view>
+
+#include <swuiexp.hxx>
+
+using namespace ::dbtools;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::uno;
+
+const char cDBFieldStart = '<';
+const char cDBFieldEnd = '>';
+
+// Helper structure for adding database rows as fields or text
+struct DB_Column
+{
+ const enum class Type { FILLTEXT, COL_FIELD, COL_TEXT, SPLITPARA } eColType;
+
+ union {
+ OUString* pText;
+ SwField* pField;
+ sal_uInt32 nFormat;
+ };
+ const SwInsDBColumn* pColInfo;
+
+ DB_Column() : eColType(Type::SPLITPARA),
+ pText(nullptr),
+ pColInfo(nullptr)
+ {}
+
+ explicit DB_Column( const OUString& rText )
+ : eColType(Type::FILLTEXT),
+ pText(new OUString(rText)),
+ pColInfo(nullptr)
+ {}
+
+ DB_Column( const SwInsDBColumn& rInfo, sal_uInt32 nFormat_ )
+ : eColType(Type::COL_TEXT),
+ nFormat(nFormat_),
+ pColInfo(&rInfo)
+ {}
+
+ DB_Column( const SwInsDBColumn& rInfo, SwDBField& rField )
+ : eColType(Type::COL_FIELD),
+ pField(&rField),
+ pColInfo(&rInfo)
+ {}
+
+ ~DB_Column()
+ {
+ if( Type::COL_FIELD == eColType )
+ delete pField;
+ else if( Type::FILLTEXT == eColType )
+ delete pText;
+ }
+};
+
+namespace {
+
+struct DB_ColumnConfigData
+{
+ SwInsDBColumns aDBColumns;
+ OUString sEdit;
+ OUString sTableList;
+ OUString sTmplNm;
+ OUString sTAutoFormatNm;
+ bool bIsTable : 1,
+ bIsField : 1,
+ bIsHeadlineOn : 1,
+ bIsEmptyHeadln : 1;
+
+ DB_ColumnConfigData(DB_ColumnConfigData const&) = delete;
+ DB_ColumnConfigData& operator=(DB_ColumnConfigData const&) = delete;
+
+ DB_ColumnConfigData()
+ {
+ bIsTable = bIsHeadlineOn = true;
+ bIsField = bIsEmptyHeadln = false;
+ }
+};
+
+}
+
+bool SwInsDBColumn::operator<( const SwInsDBColumn& rCmp ) const
+{
+ return 0 > GetAppCollator().compareString( sColumn, rCmp.sColumn );
+}
+
+SwInsertDBColAutoPilot::SwInsertDBColAutoPilot( SwView& rView,
+ Reference<XDataSource> const & xDataSource,
+ Reference<sdbcx::XColumnsSupplier> const & xColSupp,
+ const SwDBData& rData )
+ : SfxDialogController(rView.GetWindow()->GetFrameWeld(), "modules/swriter/ui/insertdbcolumnsdialog.ui", "InsertDbColumnsDialog")
+ , ConfigItem("Office.Writer/InsertData/DataSet", ConfigItemMode::NONE)
+ , aDBData(rData)
+ , sNoTmpl(SwResId(SW_STR_NONE))
+ , pView(&rView)
+ , m_xRbAsTable(m_xBuilder->weld_radio_button("astable"))
+ , m_xRbAsField(m_xBuilder->weld_radio_button("asfields"))
+ , m_xRbAsText(m_xBuilder->weld_radio_button("astext"))
+ , m_xHeadFrame(m_xBuilder->weld_frame("dbframe"))
+ , m_xLbTableDbColumn(m_xBuilder->weld_tree_view("tabledbcols"))
+ , m_xLbTextDbColumn(m_xBuilder->weld_tree_view("tabletxtcols"))
+ , m_xFormatFrame(m_xBuilder->weld_frame("formatframe"))
+ , m_xRbDbFormatFromDb(m_xBuilder->weld_radio_button("fromdatabase"))
+ , m_xRbDbFormatFromUsr(m_xBuilder->weld_radio_button("userdefined"))
+ , m_xLbDbFormatFromUsr(new NumFormatListBox(m_xBuilder->weld_combo_box("numformat")))
+ , m_xIbDbcolToEdit(m_xBuilder->weld_button("toedit"))
+ , m_xEdDbText(m_xBuilder->weld_text_view("textview"))
+ , m_xFtDbParaColl(m_xBuilder->weld_label("parastylelabel"))
+ , m_xLbDbParaColl(m_xBuilder->weld_combo_box("parastyle"))
+ , m_xIbDbcolAllTo(m_xBuilder->weld_button("oneright"))
+ , m_xIbDbcolOneTo(m_xBuilder->weld_button("allright"))
+ , m_xIbDbcolOneFrom(m_xBuilder->weld_button("oneleft"))
+ , m_xIbDbcolAllFrom(m_xBuilder->weld_button("allleft"))
+ , m_xFtTableCol(m_xBuilder->weld_label("tablecolft"))
+ , m_xLbTableCol(m_xBuilder->weld_tree_view("tablecols"))
+ , m_xCbTableHeadon(m_xBuilder->weld_check_button("tableheading"))
+ , m_xRbHeadlColnms(m_xBuilder->weld_radio_button("columnname"))
+ , m_xRbHeadlEmpty(m_xBuilder->weld_radio_button("rowonly"))
+ , m_xPbTableFormat(m_xBuilder->weld_button("tableformat"))
+ , m_xPbTableAutofmt(m_xBuilder->weld_button("autoformat"))
+{
+ m_xEdDbText->set_size_request(m_xEdDbText->get_approximate_digit_width() * 40, -1);
+ m_xLbDbParaColl->make_sorted();
+
+ nGBFormatLen = m_xFormatFrame->get_label().getLength();
+
+ if (xColSupp.is())
+ {
+ SwWrtShell& rSh = pView->GetWrtShell();
+ SvNumberFormatter* pNumFormatr = rSh.GetNumberFormatter();
+ rtl::Reference<SvNumberFormatsSupplierObj> pNumFormat = new SvNumberFormatsSupplierObj( pNumFormatr );
+ Reference< util::XNumberFormats > xDocNumberFormats = pNumFormat->getNumberFormats();
+ Reference< util::XNumberFormatTypes > xDocNumberFormatTypes(xDocNumberFormats, UNO_QUERY);
+
+ Reference<XPropertySet> xSourceProps(xDataSource, UNO_QUERY);
+ Reference< util::XNumberFormats > xNumberFormats;
+ if(xSourceProps.is())
+ {
+ Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
+ if(aFormats.hasValue())
+ {
+ Reference< util::XNumberFormatsSupplier> xSuppl;
+ aFormats >>= xSuppl;
+ if(xSuppl.is())
+ {
+ xNumberFormats = xSuppl->getNumberFormats( );
+ }
+ }
+ }
+ Reference <XNameAccess> xCols = xColSupp->getColumns();
+ const Sequence<OUString> aColNames = xCols->getElementNames();
+ for (const OUString& rColName : aColNames)
+ {
+ std::unique_ptr<SwInsDBColumn> pNew(new SwInsDBColumn( rColName ));
+ Any aCol = xCols->getByName(rColName);
+ Reference <XPropertySet> xCol;
+ aCol >>= xCol;
+ Any aType = xCol->getPropertyValue("Type");
+ sal_Int32 eDataType = 0;
+ aType >>= eDataType;
+ switch(eDataType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ case DataType::BIGINT:
+ case DataType::FLOAT:
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ {
+ pNew->bHasFormat = true;
+ Any aFormat = xCol->getPropertyValue("FormatKey");
+ if(aFormat.hasValue())
+ {
+ sal_Int32 nFormat = 0;
+ aFormat >>= nFormat;
+ if(xNumberFormats.is())
+ {
+ try
+ {
+ Reference<XPropertySet> xNumProps = xNumberFormats->getByKey( nFormat );
+ Any aFormatVal = xNumProps->getPropertyValue("FormatString");
+ Any aLocale = xNumProps->getPropertyValue("Locale");
+ OUString sFormat;
+ aFormatVal >>= sFormat;
+ lang::Locale aLoc;
+ aLocale >>= aLoc;
+ sal_Int32 nKey = xDocNumberFormats->queryKey( sFormat, aLoc, true);
+ if(nKey < 0)
+ {
+ nKey = xDocNumberFormats->addNew( sFormat, aLoc );
+ }
+ pNew->nDBNumFormat = nKey;
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("illegal number format key");
+ }
+ }
+ }
+ else
+ {
+ pNew->nDBNumFormat = getDefaultNumberFormat(xCol,
+ xDocNumberFormatTypes, LanguageTag( rSh.GetCurLang() ).getLocale());
+ }
+
+ }
+ break;
+ }
+ if( !aDBColumns.insert( std::move(pNew) ).second )
+ {
+ OSL_ENSURE( false, "Spaltenname mehrfach vergeben?" );
+ }
+ }
+ }
+
+ // fill paragraph templates-ListBox
+ {
+ SfxStyleSheetBasePool* pPool = pView->GetDocShell()->GetStyleSheetPool();
+ m_xLbDbParaColl->append_text( sNoTmpl );
+
+ const SfxStyleSheetBase* pBase = pPool->First(SfxStyleFamily::Para);
+ while( pBase )
+ {
+ m_xLbDbParaColl->append_text( pBase->GetName() );
+ pBase = pPool->Next();
+ }
+ m_xLbDbParaColl->set_active( 0 );
+ }
+
+ // when the cursor is inside of a table, table must NEVER be selectable
+ if( pView->GetWrtShell().GetTableFormat() )
+ {
+ m_xRbAsField->set_active(true);
+ m_xRbAsTable->set_sensitive(false);
+ m_xRbDbFormatFromDb->set_active(true);
+ }
+ else
+ {
+ m_xRbAsTable->set_active(true);
+ m_xRbDbFormatFromDb->set_active(true);
+ m_xIbDbcolOneFrom->set_sensitive( false );
+ m_xIbDbcolAllFrom->set_sensitive( false );
+ }
+
+ // by default, select header button
+ m_xRbHeadlColnms->set_active(true);
+ m_xRbHeadlEmpty->set_active(false);
+
+ m_xRbAsTable->connect_toggled( LINK(this, SwInsertDBColAutoPilot, PageHdl ));
+ m_xRbAsField->connect_toggled( LINK(this, SwInsertDBColAutoPilot, PageHdl ));
+ m_xRbAsText->connect_toggled( LINK(this, SwInsertDBColAutoPilot, PageHdl ));
+
+ m_xRbDbFormatFromDb->connect_toggled( LINK(this, SwInsertDBColAutoPilot, DBFormatHdl ));
+ m_xRbDbFormatFromUsr->connect_toggled( LINK(this, SwInsertDBColAutoPilot, DBFormatHdl ));
+
+ m_xPbTableFormat->connect_clicked(LINK(this, SwInsertDBColAutoPilot, TableFormatHdl ));
+ m_xPbTableAutofmt->connect_clicked(LINK(this, SwInsertDBColAutoPilot, AutoFormatHdl ));
+
+ m_xIbDbcolAllTo->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl ));
+ m_xIbDbcolOneTo->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl ));
+ m_xIbDbcolOneFrom->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl ));
+ m_xIbDbcolAllFrom->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl ));
+ m_xIbDbcolToEdit->connect_clicked( LINK(this, SwInsertDBColAutoPilot, TableToFromHdl ));
+
+ m_xCbTableHeadon->connect_toggled( LINK(this, SwInsertDBColAutoPilot, HeaderHdl ));
+
+ m_xLbTextDbColumn->connect_changed( LINK( this, SwInsertDBColAutoPilot, TVSelectHdl ));
+ m_xLbTableDbColumn->connect_changed( LINK( this, SwInsertDBColAutoPilot, TVSelectHdl ));
+ m_xLbDbFormatFromUsr->connect_changed( LINK( this, SwInsertDBColAutoPilot, CBSelectHdl ));
+ m_xLbTableCol->connect_changed( LINK( this, SwInsertDBColAutoPilot, TVSelectHdl ));
+
+ m_xLbTextDbColumn->connect_row_activated( LINK( this, SwInsertDBColAutoPilot, DblClickHdl ));
+ m_xLbTableDbColumn->connect_row_activated( LINK( this, SwInsertDBColAutoPilot, DblClickHdl ));
+ m_xLbTableCol->connect_row_activated( LINK( this, SwInsertDBColAutoPilot, DblClickHdl ));
+
+ for( size_t n = 0; n < aDBColumns.size(); ++n )
+ {
+ const OUString& rS = aDBColumns[ n ]->sColumn;
+ m_xLbTableDbColumn->append_text(rS);
+ m_xLbTextDbColumn->append_text(rS);
+ }
+ m_xLbTextDbColumn->select(0);
+ m_xLbTableDbColumn->select(0);
+
+ // read configuration
+ Load();
+
+ // lock size to widest config
+ m_xHeadFrame->set_size_request(m_xHeadFrame->get_preferred_size().Width(), -1);
+ // initialise Controls:
+ PageHdl(m_xRbAsTable->get_active() ? *m_xRbAsTable : *m_xRbAsField);
+}
+
+SwInsertDBColAutoPilot::~SwInsertDBColAutoPilot()
+{
+}
+
+IMPL_LINK( SwInsertDBColAutoPilot, PageHdl, weld::Toggleable&, rButton, void )
+{
+ if (!rButton.get_active())
+ return;
+
+ bool bShowTable = m_xRbAsTable->get_active();
+
+ weld::RadioButton& rRadio = dynamic_cast<weld::RadioButton&>(rButton);
+ m_xHeadFrame->set_label(MnemonicGenerator::EraseAllMnemonicChars(rRadio.get_label().replace('_', '~')));
+
+ m_xLbTextDbColumn->set_visible( !bShowTable );
+ m_xIbDbcolToEdit->set_visible( !bShowTable );
+ m_xEdDbText->set_visible( !bShowTable );
+ m_xFtDbParaColl->set_visible( !bShowTable );
+ m_xLbDbParaColl->set_visible( !bShowTable );
+
+ m_xLbTableDbColumn->set_visible( bShowTable );
+ m_xIbDbcolAllTo->set_visible( bShowTable );
+ m_xIbDbcolOneTo->set_visible( bShowTable );
+ m_xIbDbcolOneFrom->set_visible( bShowTable );
+ m_xIbDbcolAllFrom->set_visible( bShowTable );
+ m_xFtTableCol->set_visible( bShowTable );
+ m_xLbTableCol->set_visible( bShowTable );
+ m_xCbTableHeadon->set_visible( bShowTable );
+ m_xRbHeadlColnms->set_visible( bShowTable );
+ m_xRbHeadlEmpty->set_visible( bShowTable );
+ m_xPbTableFormat->set_visible( bShowTable );
+ m_xPbTableAutofmt->set_visible( bShowTable );
+
+ if( bShowTable )
+ m_xPbTableFormat->set_sensitive( 0 != m_xLbTableCol->n_children() );
+
+ TVSelectHdl( bShowTable ? *m_xLbTableDbColumn : *m_xLbTextDbColumn );
+}
+
+IMPL_LINK( SwInsertDBColAutoPilot, DBFormatHdl, weld::Toggleable&, rButton, void )
+{
+ if (!rButton.get_active())
+ return;
+
+ weld::TreeView& rBox = m_xRbAsTable->get_active()
+ ? ( m_xLbTableCol->get_id(0).isEmpty()
+ ? *m_xLbTableDbColumn
+ : *m_xLbTableCol )
+ : *m_xLbTextDbColumn;
+
+ SwInsDBColumn aSrch(rBox.get_selected_text());
+ SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch );
+
+ bool bFromDB = m_xRbDbFormatFromDb->get_active();
+ (*it)->bIsDBFormat = bFromDB;
+ m_xLbDbFormatFromUsr->set_sensitive( !bFromDB );
+}
+
+IMPL_LINK( SwInsertDBColAutoPilot, TableToFromHdl, weld::Button&, rButton, void )
+{
+ bool bChgEnable = true, bEnableTo = true, bEnableFrom = true;
+
+ if( &rButton == m_xIbDbcolAllTo.get() )
+ {
+ bEnableTo = false;
+
+ sal_Int32 n, nInsPos = m_xLbTableCol->get_selected_index(),
+ nCnt = m_xLbTableDbColumn->n_children();
+
+ m_xLbTableDbColumn->unselect_all();
+
+ m_xLbTableDbColumn->freeze();
+ m_xLbTableCol->freeze();
+
+ if (nInsPos == -1)
+ for( n = 0; n < nCnt; ++n )
+ m_xLbTableCol->append_text(m_xLbTableDbColumn->get_text(n));
+ else
+ for( n = 0; n < nCnt; ++n, ++nInsPos )
+ m_xLbTableCol->insert_text(nInsPos, m_xLbTableDbColumn->get_text(n));
+ m_xLbTableDbColumn->clear();
+
+ m_xLbTableDbColumn->thaw();
+ m_xLbTableCol->thaw();
+
+ m_xLbTableCol->select(nInsPos);
+ }
+ else if( &rButton == m_xIbDbcolOneTo.get() &&
+ m_xLbTableDbColumn->get_selected_index() != -1 )
+ {
+ sal_Int32 nInsPos = m_xLbTableCol->get_selected_index(),
+ nDelPos = m_xLbTableDbColumn->get_selected_index();
+ m_xLbTableCol->insert_text(nInsPos, m_xLbTableDbColumn->get_text(nDelPos));
+ m_xLbTableDbColumn->remove(nDelPos);
+
+ m_xLbTableCol->select(nInsPos);
+ if (nDelPos >= m_xLbTableDbColumn->n_children())
+ nDelPos = m_xLbTableDbColumn->n_children() - 1;
+ m_xLbTableDbColumn->select(nDelPos);
+
+ bEnableTo = 0 != m_xLbTableDbColumn->n_children();
+ }
+ else if( &rButton == m_xIbDbcolOneFrom.get() )
+ {
+ if (m_xLbTableCol->get_selected_index() != -1)
+ {
+ sal_Int32 nInsPos,
+ nDelPos = m_xLbTableCol->get_selected_index();
+
+ // look for the right InsertPos!!
+ SwInsDBColumn aSrch(m_xLbTableCol->get_text(nDelPos));
+ SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch );
+ if( it == aDBColumns.begin() || (it+1) == aDBColumns.end() )
+ nInsPos = it - aDBColumns.begin();
+ else
+ {
+ nInsPos = -1;
+ while( ++it != aDBColumns.end() &&
+ -1 == (nInsPos = m_xLbTableDbColumn->
+ find_text( (*it)->sColumn )) )
+ ;
+ }
+
+ m_xLbTableDbColumn->insert_text(nInsPos, aSrch.sColumn);
+ m_xLbTableCol->remove( nDelPos );
+
+ if (nInsPos >= m_xLbTableDbColumn->n_children())
+ nInsPos = m_xLbTableDbColumn->n_children() - 1;
+ m_xLbTableDbColumn->select(nInsPos);
+
+ if (nDelPos >= m_xLbTableCol->n_children())
+ nDelPos = m_xLbTableCol->n_children() - 1;
+ m_xLbTableCol->select(nDelPos);
+ }
+ else
+ bEnableTo = 0 != m_xLbTableDbColumn->n_children();
+
+ bEnableFrom = 0 != m_xLbTableCol->n_children();
+ }
+ else if( &rButton == m_xIbDbcolAllFrom.get() )
+ {
+ bEnableFrom = false;
+
+ m_xLbTableDbColumn->freeze();
+ m_xLbTableCol->freeze();
+
+ m_xLbTableDbColumn->clear();
+ m_xLbTableCol->clear();
+ for (size_t n = 0; n < aDBColumns.size(); ++n)
+ m_xLbTableDbColumn->append_text(aDBColumns[n]->sColumn);
+
+ m_xLbTableDbColumn->thaw();
+ m_xLbTableCol->thaw();
+
+ m_xLbTableDbColumn->select(0);
+ }
+ else if( &rButton == m_xIbDbcolToEdit.get() )
+ {
+ bChgEnable = false;
+ // move data to Edit:
+ OUString aField(m_xLbTextDbColumn->get_selected_text());
+ if( !aField.isEmpty() )
+ {
+ OUString aStr( m_xEdDbText->get_text() );
+ int nStartPos, nEndPos;
+ m_xEdDbText->get_selection_bounds(nStartPos, nEndPos);
+ sal_Int32 nPos = std::min(nStartPos, nEndPos);
+ sal_Int32 nMax = std::max(nStartPos, nEndPos);
+ const sal_Int32 nSel = nMax - nPos;
+ if( nSel )
+ // first delete the existing selection
+ aStr = aStr.replaceAt( nPos, nSel, u"" );
+
+ aField = OUStringChar(cDBFieldStart) + aField + OUStringChar(cDBFieldEnd);
+ if( !aStr.isEmpty() )
+ {
+ if( nPos ) // one blank in front
+ {
+ sal_Unicode c = aStr[ nPos-1 ];
+ if( '\n' != c && '\r' != c )
+ aField = " " + aField;
+ }
+ if( nPos < aStr.getLength() ) // one blank behind
+ {
+ sal_Unicode c = aStr[ nPos ];
+ if( '\n' != c && '\r' != c )
+ aField += " ";
+ }
+ }
+
+ m_xEdDbText->set_text( aStr.replaceAt( nPos, 0, aField ) );
+ nPos += aField.getLength();
+ m_xEdDbText->select_region(nPos, nPos);
+ }
+ }
+
+ if( !bChgEnable )
+ return;
+
+ m_xIbDbcolOneTo->set_sensitive( bEnableTo );
+ m_xIbDbcolAllTo->set_sensitive( bEnableTo );
+ m_xIbDbcolOneFrom->set_sensitive( bEnableFrom );
+ m_xIbDbcolAllFrom->set_sensitive( bEnableFrom );
+
+ m_xRbDbFormatFromDb->set_sensitive( false );
+ m_xRbDbFormatFromUsr->set_sensitive( false );
+ m_xLbDbFormatFromUsr->set_sensitive( false );
+
+ m_xPbTableFormat->set_sensitive( bEnableFrom );
+}
+
+IMPL_LINK(SwInsertDBColAutoPilot, DblClickHdl, weld::TreeView&, rBox, bool)
+{
+ weld::Button* pButton = nullptr;
+ if( &rBox == m_xLbTextDbColumn.get() )
+ pButton = m_xIbDbcolToEdit.get();
+ else if( &rBox == m_xLbTableDbColumn.get() && m_xIbDbcolOneTo->get_sensitive() )
+ pButton = m_xIbDbcolOneTo.get();
+ else if( &rBox == m_xLbTableCol.get() && m_xIbDbcolOneFrom->get_sensitive() )
+ pButton = m_xIbDbcolOneFrom.get();
+
+ if (pButton)
+ TableToFromHdl(*pButton);
+
+ return true;
+}
+
+IMPL_LINK_NOARG(SwInsertDBColAutoPilot, TableFormatHdl, weld::Button&, void)
+{
+ SwWrtShell& rSh = pView->GetWrtShell();
+ bool bNewSet = false;
+ if( !pTableSet )
+ {
+ bNewSet = true;
+ pTableSet.reset(new SfxItemSet( rSh.GetAttrPool(), SwuiGetUITableAttrRange() ));
+
+ // At first acquire the simple attributes
+ pTableSet->Put( SfxStringItem( FN_PARAM_TABLE_NAME, rSh.GetUniqueTableName() ));
+ pTableSet->Put( SfxUInt16Item( FN_PARAM_TABLE_HEADLINE, 1 ) );
+
+ pTableSet->Put( SfxUInt16Item( SID_BACKGRND_DESTINATION,
+ rSh.GetViewOptions()->GetTableDest() ));
+
+ SvxBrushItem aBrush( RES_BACKGROUND );
+ pTableSet->Put( aBrush );
+ aBrush.SetWhich(SID_ATTR_BRUSH_ROW);
+ pTableSet->Put( aBrush );
+ aBrush.SetWhich(SID_ATTR_BRUSH_TABLE);
+ pTableSet->Put( aBrush );
+
+ SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER );
+ // table variant, when multiple table cells are selected
+ aBoxInfo.SetTable( true );
+ // always show gap field
+ aBoxInfo.SetDist( true);
+ // set minimum size in tables and paragraphs
+ aBoxInfo.SetMinDist( false );
+ // always set default-gap
+ aBoxInfo.SetDefDist( MIN_BORDER_DIST );
+ // Single lines can have DontCare-status only in tables
+ aBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISABLE );
+ pTableSet->Put( aBoxInfo );
+
+ SwGetCurColNumPara aPara;
+ const sal_uInt16 nNum = rSh.GetCurColNum( &aPara );
+ tools::Long nWidth;
+
+ if( nNum )
+ {
+ nWidth = aPara.pPrtRect->Width();
+ const SwFormatCol& rCol = aPara.pFrameFormat->GetCol();
+ const SwColumns& rCols = rCol.GetColumns();
+
+ // initialise nStart and nEnd for nNum == 0
+ tools::Long nWidth1 = 0,
+ nStart1 = 0,
+ nEnd1 = nWidth;
+ for( sal_uInt16 i = 0; i < nNum; ++i )
+ {
+ const SwColumn* pCol = &rCols[i];
+ nStart1 = pCol->GetLeft() + nWidth1;
+ nWidth1 += static_cast<tools::Long>(rCol.CalcColWidth( i, o3tl::narrowing<sal_uInt16>(nWidth) ));
+ nEnd1 = nWidth1 - pCol->GetRight();
+ }
+ if(nStart1 || nEnd1 != nWidth)
+ nWidth = nEnd1 - nStart1;
+ }
+ else
+ nWidth = rSh.GetAnyCurRect(
+ (FrameTypeFlags::FLY_ANY & rSh.GetFrameType( nullptr, true ))
+ ? CurRectType::FlyEmbeddedPrt
+ : CurRectType::PagePrt ).Width();
+
+ SwTabCols aTabCols;
+ aTabCols.SetRight( nWidth );
+ aTabCols.SetRightMax( nWidth );
+ pRep.reset(new SwTableRep( aTabCols ));
+ pRep->SetAlign( text::HoriOrientation::NONE );
+ pRep->SetSpace( nWidth );
+ pRep->SetWidth( nWidth );
+ pRep->SetWidthPercent( 100 );
+ pTableSet->Put( SwPtrItem( FN_TABLE_REP, pRep.get() ));
+
+ pTableSet->Put( SfxUInt16Item( SID_HTML_MODE,
+ ::GetHtmlMode( pView->GetDocShell() )));
+ }
+
+ sal_Int32 nCols = m_xLbTableCol->n_children();
+ if (nCols != pRep->GetAllColCount() && nCols > 0)
+ {
+ // Number of columns has changed: then the TabCols have to be adjusted
+ tools::Long nWidth = pRep->GetWidth();
+ --nCols;
+ SwTabCols aTabCols( nCols );
+ aTabCols.SetRight( nWidth );
+ aTabCols.SetRightMax( nWidth );
+ if( nCols )
+ {
+ const sal_Int32 nStep = nWidth / (nCols+1);
+ for( sal_Int32 n = 0; n < nCols; ++n )
+ {
+ aTabCols.Insert( nStep*(n+1), false, n );
+ }
+ }
+ pRep.reset(new SwTableRep( aTabCols ));
+ pRep->SetAlign( text::HoriOrientation::NONE );
+ pRep->SetSpace( nWidth );
+ pRep->SetWidth( nWidth );
+ pRep->SetWidthPercent( 100 );
+ pTableSet->Put( SwPtrItem( FN_TABLE_REP, pRep.get() ));
+ }
+
+ SwAbstractDialogFactory& rFact = swui::GetFactory();
+
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(rFact.CreateSwTableTabDlg(m_xDialog.get(), pTableSet.get(), &rSh));
+ if( RET_OK == pDlg->Execute() )
+ pTableSet->Put( *pDlg->GetOutputItemSet() );
+ else if( bNewSet )
+ {
+ pTableSet.reset();
+ pRep.reset();
+ }
+}
+
+IMPL_LINK_NOARG(SwInsertDBColAutoPilot, AutoFormatHdl, weld::Button&, void)
+{
+ SwAbstractDialogFactory& rFact = swui::GetFactory();
+
+ ScopedVclPtr<AbstractSwAutoFormatDlg> pDlg(rFact.CreateSwAutoFormatDlg(m_xDialog.get(), pView->GetWrtShellPtr(), false, m_xTAutoFormat.get()));
+ if( RET_OK == pDlg->Execute())
+ m_xTAutoFormat = pDlg->FillAutoFormatOfIndex();
+}
+
+IMPL_LINK(SwInsertDBColAutoPilot, TVSelectHdl, weld::TreeView&, rBox, void)
+{
+ weld::TreeView* pGetBox = &rBox;
+
+ SwInsDBColumn aSrch(pGetBox->get_selected_text());
+ SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch );
+
+ // set the selected FieldName at the FormatGroupBox, so that
+ // it's clear what field is configured by the format!
+ OUString sText( m_xFormatFrame->get_label().copy( 0, nGBFormatLen ));
+ if( aSrch.sColumn.isEmpty() )
+ {
+ m_xRbDbFormatFromDb->set_sensitive( false );
+ m_xRbDbFormatFromUsr->set_sensitive( false );
+ m_xLbDbFormatFromUsr->set_sensitive( false );
+ }
+ else
+ {
+ bool bEnableFormat = (*it)->bHasFormat;
+ m_xRbDbFormatFromDb->set_sensitive( bEnableFormat );
+ m_xRbDbFormatFromUsr->set_sensitive( bEnableFormat );
+
+ if( bEnableFormat )
+ {
+ sText += " (" + aSrch.sColumn + ")";
+ }
+
+ bool bIsDBFormat = (*it)->bIsDBFormat;
+ m_xRbDbFormatFromDb->set_active( bIsDBFormat );
+ m_xRbDbFormatFromUsr->set_active( !bIsDBFormat );
+ m_xLbDbFormatFromUsr->set_sensitive( !bIsDBFormat );
+ if( !bIsDBFormat )
+ m_xLbDbFormatFromUsr->SetDefFormat( (*it)->nUsrNumFormat );
+ }
+
+ m_xFormatFrame->set_label(sText);
+
+ if (m_xLbTableCol->n_children())
+ {
+ // to know later on, what ListBox was the "active", a Flag
+ // is remembered in the 1st entry
+ if (&rBox == m_xLbTableCol.get())
+ m_xLbTableCol->set_id(0, "tablecols");
+ else
+ m_xLbTableCol->set_id(0, OUString());
+ }
+}
+
+IMPL_LINK_NOARG(SwInsertDBColAutoPilot, CBSelectHdl, weld::ComboBox&, void)
+{
+ weld::TreeView* pGetBox = m_xRbAsTable->get_active()
+ ? ( m_xLbTableCol->get_id(0).isEmpty()
+ ? m_xLbTableDbColumn.get()
+ : m_xLbTableCol.get() )
+ : m_xLbTextDbColumn.get();
+
+ SwInsDBColumn aSrch(pGetBox->get_selected_text());
+ SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch );
+
+ if( !aSrch.sColumn.isEmpty() )
+ {
+ m_xLbDbFormatFromUsr->CallSelectHdl();
+ (*it)->nUsrNumFormat = m_xLbDbFormatFromUsr->GetFormat();
+ }
+}
+
+IMPL_LINK_NOARG(SwInsertDBColAutoPilot, HeaderHdl, weld::Toggleable&, void)
+{
+ bool bEnable = m_xCbTableHeadon->get_active();
+ m_xRbHeadlColnms->set_sensitive( bEnable );
+ m_xRbHeadlEmpty->set_sensitive( bEnable );
+}
+
+static void lcl_InsTextInArr( const OUString& rText, DB_Columns& rColArr )
+{
+ sal_Int32 nSttPos = 0, nFndPos;
+ while( -1 != ( nFndPos = rText.indexOf( '\x0A', nSttPos )) )
+ {
+ if( 1 < nFndPos )
+ {
+ rColArr.push_back(std::make_unique<DB_Column>(rText.copy(nSttPos, nFndPos -1)));
+ }
+ rColArr.push_back(std::make_unique<DB_Column>());
+ nSttPos = nFndPos + 1;
+ }
+ if( nSttPos < rText.getLength() )
+ {
+ rColArr.push_back(std::make_unique<DB_Column>(rText.copy(nSttPos)));
+ }
+}
+
+bool SwInsertDBColAutoPilot::SplitTextToColArr( const OUString& rText,
+ DB_Columns& rColArr,
+ bool bInsField )
+{
+ // create each of the database columns from the text again
+ // and then save in an array
+ // database columns are in <> and must be present in the columns' array:
+ OUString sText( rText );
+ sal_Int32 nFndPos, nEndPos, nSttPos = 0;
+
+ while( -1 != ( nFndPos = sText.indexOf( cDBFieldStart, nSttPos )))
+ {
+ nSttPos = nFndPos + 1;
+ nEndPos = sText.indexOf( cDBFieldEnd, nSttPos+1 );
+ if( -1 != nEndPos )
+ {
+ // Text in <> brackets found: what is it:
+ SwInsDBColumn aSrch( sText.copy( nSttPos, nEndPos - nSttPos ));
+ SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch );
+ if( it != aDBColumns.end() )
+ {
+ // that is a valid field
+ // so surely the text "before":
+ const SwInsDBColumn& rFndCol = **it;
+
+ DB_Column* pNew;
+
+ if( 1 < nSttPos )
+ {
+ ::lcl_InsTextInArr( sText.copy( 0, nSttPos-1 ), rColArr );
+ sText = sText.copy( nSttPos-1 );
+ }
+
+ sText = sText.copy( rFndCol.sColumn.getLength() + 2 );
+ nSttPos = 0;
+
+ sal_uInt16 nSubType = 0;
+ sal_uInt32 nFormat;
+ if( rFndCol.bHasFormat )
+ {
+ if( rFndCol.bIsDBFormat )
+ nFormat = static_cast<sal_uInt32>(rFndCol.nDBNumFormat);
+ else
+ {
+ nFormat = rFndCol.nUsrNumFormat;
+ nSubType = nsSwExtendedSubType::SUB_OWN_FMT;
+ }
+ }
+ else
+ nFormat = 0;
+
+ if( bInsField )
+ {
+ SwWrtShell& rSh = pView->GetWrtShell();
+ SwDBFieldType aFieldType( rSh.GetDoc(), aSrch.sColumn,
+ aDBData );
+ pNew = new DB_Column( rFndCol, *new SwDBField(
+ static_cast<SwDBFieldType*>(rSh.InsertFieldType( aFieldType )),
+ nFormat ) );
+ if( nSubType )
+ pNew->pField->SetSubType( nSubType );
+ }
+ else
+ pNew = new DB_Column( rFndCol, nFormat );
+
+ rColArr.push_back( std::unique_ptr<DB_Column>(pNew) );
+ }
+ }
+ }
+
+ // don't forget the last text
+ if( !sText.isEmpty() )
+ ::lcl_InsTextInArr( sText, rColArr );
+
+ return !rColArr.empty();
+}
+
+void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection,
+ Reference< XDataSource> const & xSource,
+ Reference< XConnection> const & xConnection,
+ Reference< sdbc::XResultSet > const & xResultSet_in )
+{
+ auto xResultSet = xResultSet_in;
+
+ const Any* pSelection = rSelection.hasElements() ? rSelection.getConstArray() : nullptr;
+ SwWrtShell& rSh = pView->GetWrtShell();
+
+ //with the drag and drop interface no result set is initially available
+ bool bDisposeResultSet = false;
+ // we don't have a cursor, so we have to create our own RowSet
+ if ( !xResultSet.is() )
+ {
+ xResultSet = SwDBManager::createCursor(aDBData.sDataSource,aDBData.sCommand,aDBData.nCommandType,xConnection,pView);
+ bDisposeResultSet = xResultSet.is();
+ }
+
+ Reference< sdbc::XRow > xRow(xResultSet, UNO_QUERY);
+ if ( !xRow.is() )
+ return;
+
+ rSh.StartAllAction();
+ bool bUndo = rSh.DoesUndo();
+ if( bUndo )
+ rSh.StartUndo();
+
+ bool bAsTable = m_xRbAsTable->get_active();
+ SvNumberFormatter& rNumFormatr = *rSh.GetNumberFormatter();
+
+ if( rSh.HasSelection() )
+ rSh.DelRight();
+
+ std::unique_ptr<SwWait> pWait;
+
+ Reference< XColumnsSupplier > xColsSupp( xResultSet, UNO_QUERY );
+ Reference <XNameAccess> xCols = xColsSupp->getColumns();
+
+ uno::Reference<sdbcx::XRowLocate> xRowLocate(xResultSet, uno::UNO_QUERY_THROW);
+
+ do{ // middle checked loop!!
+ if( bAsTable ) // fill in data as table
+ {
+ rSh.DoUndo( false );
+
+ sal_Int32 nCols = m_xLbTableCol->n_children();
+ sal_Int32 nRows = 0;
+ if( m_xCbTableHeadon->get_active() )
+ nRows++;
+
+ if( pSelection )
+ nRows += rSelection.getLength();
+ else
+ ++nRows;
+
+ // prepare the array for the selected columns
+ std::vector<SwInsDBColumn*> aColFields;
+ for( sal_Int32 n = 0; n < nCols; ++n )
+ {
+ SwInsDBColumn aSrch(m_xLbTableCol->get_text(n));
+ SwInsDBColumns::const_iterator it = aDBColumns.find( &aSrch );
+ if (it != aDBColumns.end())
+ aColFields.push_back(it->get());
+ else {
+ OSL_ENSURE( false, "database column not found" );
+ }
+ }
+
+ if( static_cast<size_t>(nCols) != aColFields.size() )
+ {
+ OSL_ENSURE( false, "not all database columns found" );
+ nCols = static_cast<sal_Int32>(aColFields.size());
+ }
+
+ if(!nRows || !nCols)
+ {
+ OSL_ENSURE( false, "wrong parameters" );
+ break;
+ }
+
+ const SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig();
+
+ bool bHTML = 0 != (::GetHtmlMode( pView->GetDocShell() ) & HTMLMODE_ON);
+ rSh.InsertTable(
+ pModOpt->GetInsTableFlags(bHTML),
+ nRows, nCols, (pSelection ? m_xTAutoFormat.get(): nullptr) );
+ rSh.MoveTable( GotoPrevTable, fnTableStart );
+
+ if( pSelection && pTableSet )
+ SetTabSet();
+
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aTableSet( rSh.GetAttrPool() );
+ bool bIsAutoUpdateCells = rSh.IsAutoUpdateCells();
+ rSh.SetAutoUpdateCells( false );
+
+ if( m_xCbTableHeadon->get_active() )
+ {
+ for( sal_Int32 n = 0; n < nCols; ++n )
+ {
+ if( m_xRbHeadlColnms->get_active() )
+ {
+ rSh.SwEditShell::Insert2( aColFields[ n ]->sColumn );
+ }
+ rSh.GoNextCell();
+ }
+ }
+ else
+ rSh.SetRowsToRepeat( 0 );
+
+ for( sal_Int32 i = 0 ; ; ++i )
+ {
+ bool bBreak = false;
+ try
+ {
+ if(pSelection)
+ {
+ bBreak = !xRowLocate->moveToBookmark(pSelection[i]);
+ }
+ else if(!i)
+ bBreak = !xResultSet->first();
+ }
+ catch (const Exception&)
+ {
+ bBreak = true;
+ }
+ if(bBreak)
+ break;
+
+ for( sal_Int32 n = 0; n < nCols; ++n )
+ {
+ // at the very first time, NO GoNextCell, because we're
+ // already in it. Also no GoNextCell after the Insert,
+ // because an empty line is added at the end.
+ if( i || n )
+ rSh.GoNextCell();
+
+ const SwInsDBColumn* pEntry = aColFields[ n ];
+
+ Reference< XColumn > xColumn;
+ xCols->getByName(pEntry->sColumn) >>= xColumn;
+ Reference< XPropertySet > xColumnProps( xColumn, UNO_QUERY );
+ sal_Int32 eDataType = 0;
+ if( xColumnProps.is() )
+ {
+ Any aType = xColumnProps->getPropertyValue("Type");
+ aType >>= eDataType;
+ }
+ try
+ {
+ if( pEntry->bHasFormat )
+ {
+ SwTableBoxNumFormat aNumFormat(
+ pEntry->bIsDBFormat ? static_cast<sal_uInt32>(pEntry->nDBNumFormat)
+ : pEntry->nUsrNumFormat );
+ aTableSet.Put(aNumFormat);
+ if( xColumn.is() )
+ {
+ double fVal = xColumn->getDouble();
+ if( xColumn->wasNull() )
+ aTableSet.ClearItem( RES_BOXATR_VALUE );
+ else
+ {
+ if(rNumFormatr.GetType(aNumFormat.GetValue()) & SvNumFormatType::DATE)
+ {
+ ::Date aStandard(1,1,1900);
+ if (rNumFormatr.GetNullDate() != aStandard)
+ fVal += (aStandard - rNumFormatr.GetNullDate());
+ }
+ aTableSet.Put( SwTableBoxValue( fVal ));
+ }
+ }
+ else
+ aTableSet.ClearItem( RES_BOXATR_VALUE );
+ rSh.SetTableBoxFormulaAttrs( aTableSet );
+ }
+ //#i60207# don't insert binary data as string - creates a loop
+ else if( DataType::BINARY == eDataType ||
+ DataType::VARBINARY == eDataType ||
+ DataType::LONGVARBINARY== eDataType ||
+ DataType::SQLNULL == eDataType ||
+ DataType::OTHER == eDataType ||
+ DataType::OBJECT == eDataType ||
+ DataType::DISTINCT == eDataType ||
+ DataType::STRUCT == eDataType ||
+ DataType::ARRAY == eDataType ||
+ DataType::BLOB == eDataType ||
+ DataType::CLOB == eDataType ||
+ DataType::REF == eDataType
+ )
+ {
+ // do nothing
+ }
+ else
+ {
+ const OUString sVal = xColumn->getString();
+ if(!xColumn->wasNull())
+ {
+ rSh.SwEditShell::Insert2( sVal );
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ }
+ }
+
+ if( !pSelection )
+ {
+ if ( !xResultSet->next() )
+ break;
+ }
+ else if( i+1 >= rSelection.getLength() )
+ break;
+
+ if( 10 == i )
+ pWait.reset(new SwWait( *pView->GetDocShell(), true ));
+ }
+
+ rSh.MoveTable( GotoCurrTable, fnTableStart );
+ if( !pSelection && ( pTableSet || m_xTAutoFormat ))
+ {
+ if( pTableSet )
+ SetTabSet();
+
+ if (m_xTAutoFormat)
+ rSh.SetTableStyle(*m_xTAutoFormat);
+ }
+ rSh.SetAutoUpdateCells( bIsAutoUpdateCells );
+ }
+ else // add data as fields/text
+ {
+ DB_Columns aColArr;
+ if( SplitTextToColArr( m_xEdDbText->get_text(), aColArr, m_xRbAsField->get_active() ) )
+ {
+ // now for each data set, we can iterate over the array
+ // and add the data
+
+ if( !rSh.IsSttPara() )
+ rSh.SwEditShell::SplitNode();
+ if( !rSh.IsEndPara() )
+ {
+ rSh.SwEditShell::SplitNode();
+ rSh.SwCursorShell::Left(1,CRSR_SKIP_CHARS);
+ }
+
+ rSh.DoUndo( false );
+
+ SwTextFormatColl* pColl = nullptr;
+ {
+ const OUString sTmplNm(m_xLbDbParaColl->get_active_text());
+ if( sNoTmpl != sTmplNm )
+ {
+ pColl = rSh.FindTextFormatCollByName( sTmplNm );
+ if( !pColl )
+ {
+ const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(
+ sTmplNm, SwGetPoolIdFromName::TxtColl );
+ if( USHRT_MAX != nId )
+ pColl = rSh.GetTextCollFromPool( nId );
+ else
+ pColl = rSh.MakeTextFormatColl( sTmplNm );
+ }
+ rSh.SetTextFormatColl( pColl );
+ }
+ }
+
+ // for adding as fields -> insert a "NextField" after
+ // every data set
+ SwDBFormatData aDBFormatData;
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ aDBFormatData.xFormatter.set(util::NumberFormatter::create(xContext), UNO_QUERY_THROW) ;
+
+ Reference<XPropertySet> xSourceProps(xSource, UNO_QUERY);
+ if(xSourceProps.is())
+ {
+ Any aFormats = xSourceProps->getPropertyValue("NumberFormatsSupplier");
+ if(aFormats.hasValue())
+ {
+ Reference< util::XNumberFormatsSupplier> xSuppl;
+ aFormats >>= xSuppl;
+ if(xSuppl.is())
+ {
+ Reference< XPropertySet > xSettings = xSuppl->getNumberFormatSettings();
+ Any aNull = xSettings->getPropertyValue("NullDate");
+ aNull >>= aDBFormatData.aNullDate;
+ if(aDBFormatData.xFormatter.is())
+ aDBFormatData.xFormatter->attachNumberFormatsSupplier(xSuppl);
+ }
+ }
+ }
+ aDBFormatData.aLocale = LanguageTag( rSh.GetCurLang() ).getLocale();
+ SwDBNextSetField aNxtDBField( static_cast<SwDBNextSetFieldType*>(rSh.
+ GetFieldType( 0, SwFieldIds::DbNextSet )),
+ "1", aDBData );
+
+ bool bSetCursor = true;
+ const size_t nCols = aColArr.size();
+ ::sw::mark::IMark* pMark = nullptr;
+ for( sal_Int32 i = 0 ; ; ++i )
+ {
+ bool bBreak = false;
+ try
+ {
+ if(pSelection)
+ {
+ bBreak = !xRowLocate->moveToBookmark(pSelection[i]);
+ }
+ else if(!i)
+ bBreak = !xResultSet->first();
+ }
+ catch (const Exception&)
+ {
+ bBreak = true;
+ }
+
+ if(bBreak)
+ break;
+
+ for( size_t n = 0; n < nCols; ++n )
+ {
+ DB_Column* pDBCol = aColArr[ n ].get();
+ OUString sIns;
+ switch( pDBCol->eColType )
+ {
+ case DB_Column::Type::FILLTEXT:
+ sIns = *pDBCol->pText;
+ break;
+
+ case DB_Column::Type::SPLITPARA:
+ rSh.SplitNode();
+ // when the template is not the same as the follow template,
+ // the selected has to be set newly
+ if( pColl && &pColl->GetNextTextFormatColl() != pColl )
+ rSh.SetTextFormatColl( pColl );
+ break;
+
+ case DB_Column::Type::COL_FIELD:
+ {
+ std::unique_ptr<SwDBField> pField(static_cast<SwDBField *>(
+ pDBCol->pField->CopyField().release()));
+ double nValue = DBL_MAX;
+
+ Reference< XPropertySet > xColumnProps;
+ xCols->getByName(pDBCol->pColInfo->sColumn) >>= xColumnProps;
+
+ pField->SetExpansion( SwDBManager::GetDBField(
+ xColumnProps,
+ aDBFormatData,
+ &nValue ) );
+ if( DBL_MAX != nValue )
+ {
+ Any aType = xColumnProps->getPropertyValue("Type");
+ sal_Int32 eDataType = 0;
+ aType >>= eDataType;
+ if( DataType::DATE == eDataType || DataType::TIME == eDataType ||
+ DataType::TIMESTAMP == eDataType)
+
+ {
+ ::Date aStandard(1,1,1900);
+ ::Date aCompare(aDBFormatData.aNullDate.Day ,
+ aDBFormatData.aNullDate.Month,
+ aDBFormatData.aNullDate.Year);
+ if(aStandard != aCompare)
+ nValue += (aStandard - aCompare);
+ }
+ pField->ChgValue( nValue, true );
+ }
+ pField->SetInitialized();
+
+ rSh.InsertField2( *pField );
+ }
+ break;
+
+ case DB_Column::Type::COL_TEXT:
+ {
+ double nValue = DBL_MAX;
+ Reference< XPropertySet > xColumnProps;
+ xCols->getByName(pDBCol->pColInfo->sColumn) >>= xColumnProps;
+ sIns = SwDBManager::GetDBField(
+ xColumnProps,
+ aDBFormatData,
+ &nValue );
+ if( pDBCol->nFormat &&
+ DBL_MAX != nValue )
+ {
+ const Color* pCol;
+ if(rNumFormatr.GetType(pDBCol->nFormat) & SvNumFormatType::DATE)
+ {
+ ::Date aStandard(1,1,1900);
+ if (rNumFormatr.GetNullDate() != aStandard)
+ nValue += (aStandard - rNumFormatr.GetNullDate());
+ }
+ rNumFormatr.GetOutputString( nValue,
+ pDBCol->nFormat,
+ sIns, &pCol );
+ }
+ }
+ break;
+ }
+
+ if( !sIns.isEmpty() )
+ {
+ rSh.Insert( sIns );
+
+ if( bSetCursor)
+ {
+ // to the beginning and set a mark, so that
+ // the cursor can be set to the initial position
+ // at the end.
+
+ rSh.SwCursorShell::MovePara(
+ GoCurrPara, fnParaStart );
+ pMark = rSh.SetBookmark(
+ vcl::KeyCode(),
+ OUString(),
+ IDocumentMarkAccess::MarkType::UNO_BOOKMARK );
+ rSh.SwCursorShell::MovePara(
+ GoCurrPara, fnParaEnd );
+ bSetCursor = false;
+ }
+ }
+ }
+
+ if( !pSelection )
+ {
+ bool bNext = xResultSet->next();
+ if(!bNext)
+ break;
+ }
+ else if( i+1 >= rSelection.getLength() )
+ break;
+
+ if( m_xRbAsField->get_active() )
+ rSh.InsertField2( aNxtDBField );
+
+ if( !rSh.IsSttPara() )
+ rSh.SwEditShell::SplitNode();
+
+ if( 10 == i )
+ pWait.reset(new SwWait( *pView->GetDocShell(), true ));
+ }
+
+ if( !bSetCursor && pMark != nullptr)
+ {
+ rSh.SetMark();
+ rSh.GotoMark( pMark );
+ rSh.getIDocumentMarkAccess()->deleteMark( pMark );
+ break;
+ }
+ }
+ }
+ // write configuration
+ Commit();
+ }while( false ); // middle checked loop
+
+ if( bUndo )
+ {
+ rSh.DoUndo();
+ rSh.AppendUndoForInsertFromDB( bAsTable );
+ rSh.EndUndo();
+ }
+ rSh.ClearMark();
+ rSh.EndAllAction();
+
+ if ( bDisposeResultSet )
+ ::comphelper::disposeComponent(xResultSet);
+}
+
+void SwInsertDBColAutoPilot::SetTabSet()
+{
+ SwWrtShell& rSh = pView->GetWrtShell();
+ const SfxPoolItem* pItem;
+
+ if (m_xTAutoFormat)
+ {
+ if (m_xTAutoFormat->IsFrame())
+ {
+ // border is from AutoFormat
+ pTableSet->ClearItem( RES_BOX );
+ pTableSet->ClearItem( SID_ATTR_BORDER_INNER );
+ }
+ if (m_xTAutoFormat->IsBackground())
+ {
+ pTableSet->ClearItem( RES_BACKGROUND );
+ pTableSet->ClearItem( SID_ATTR_BRUSH_ROW );
+ pTableSet->ClearItem( SID_ATTR_BRUSH_TABLE );
+ }
+ }
+ else
+ {
+ // remove the defaults again, it makes no sense to set them
+ SvxBrushItem aBrush( RES_BACKGROUND );
+ static const sal_uInt16 aIds[3] =
+ { RES_BACKGROUND, SID_ATTR_BRUSH_ROW, SID_ATTR_BRUSH_TABLE };
+ for(sal_uInt16 i : aIds)
+ if( SfxItemState::SET == pTableSet->GetItemState( i,
+ false, &pItem ) && *pItem == aBrush )
+ pTableSet->ClearItem( i );
+ }
+
+ const SfxStringItem* pTableNameItem = pTableSet->GetItemIfSet( FN_PARAM_TABLE_NAME, false);
+ if( pTableNameItem && pTableNameItem->GetValue() == rSh.GetTableFormat()->GetName() )
+ pTableSet->ClearItem( FN_PARAM_TABLE_NAME );
+
+ rSh.MoveTable( GotoCurrTable, fnTableStart );
+ rSh.SetMark();
+ rSh.MoveTable( GotoCurrTable, fnTableEnd );
+
+ ItemSetToTableParam( *pTableSet, rSh );
+
+ rSh.ClearMark();
+ rSh.MoveTable( GotoCurrTable, fnTableStart );
+}
+
+static Sequence<OUString> lcl_createSourceNames(std::u16string_view rNodeName)
+{
+ Sequence<OUString> aSourceNames(11);
+ OUString* pNames = aSourceNames.getArray();
+ pNames[0] = OUString::Concat(rNodeName) + "/DataSource";
+ pNames[1] = OUString::Concat(rNodeName) + "/Command";
+ pNames[2] = OUString::Concat(rNodeName) + "/CommandType";
+ pNames[3] = OUString::Concat(rNodeName) + "/ColumnsToText";
+ pNames[4] = OUString::Concat(rNodeName) + "/ColumnsToTable";
+ pNames[5] = OUString::Concat(rNodeName) + "/ParaStyle";
+ pNames[6] = OUString::Concat(rNodeName) + "/TableAutoFormat";
+ pNames[7] = OUString::Concat(rNodeName) + "/IsTable";
+ pNames[8] = OUString::Concat(rNodeName) + "/IsField";
+ pNames[9] = OUString::Concat(rNodeName) + "/IsHeadlineOn";
+ pNames[10] = OUString::Concat(rNodeName) + "/IsEmptyHeadline";
+ return aSourceNames;
+}
+
+static Sequence<OUString> lcl_CreateSubNames(std::u16string_view rSubNodeName)
+{
+ return
+ {
+ OUString::Concat(rSubNodeName) + "/ColumnName",
+ OUString::Concat(rSubNodeName) + "/ColumnIndex",
+ OUString::Concat(rSubNodeName) + "/IsNumberFormat",
+ OUString::Concat(rSubNodeName) + "/IsNumberFormatFromDataBase",
+ OUString::Concat(rSubNodeName) + "/NumberFormat",
+ OUString::Concat(rSubNodeName) + "/NumberFormatLocale"
+ };
+}
+
+static OUString lcl_CreateUniqueName(const Sequence<OUString>& aNames)
+{
+ sal_Int32 nIdx = aNames.getLength();
+ while(true)
+ {
+ const OUString sRet = "_" + OUString::number(nIdx++);
+ if ( comphelper::findValue(aNames, sRet) == -1 )
+ return sRet; // No match found, return unique name
+ }
+}
+
+void SwInsertDBColAutoPilot::Notify( const css::uno::Sequence< OUString >& ) {}
+
+void SwInsertDBColAutoPilot::ImplCommit()
+{
+ Sequence <OUString> aNames = GetNodeNames(OUString());
+ //remove entries that contain this data source + table at first
+ for(OUString const & nodeName : std::as_const(aNames))
+ {
+ Sequence<Any> aSourceProperties = GetProperties({ nodeName + "/DataSource", nodeName + "/Command" });
+ const Any* pSourceProps = aSourceProperties.getArray();
+ OUString sSource, sCommand;
+ pSourceProps[0] >>= sSource;
+ pSourceProps[1] >>= sCommand;
+ if(sSource==aDBData.sDataSource && sCommand==aDBData.sCommand)
+ {
+ ClearNodeElements(OUString(), { nodeName });
+ }
+ }
+
+ aNames = GetNodeNames(OUString());
+ OUString sNewNode = lcl_CreateUniqueName(aNames);
+ Sequence<OUString> aNodeNames = lcl_createSourceNames(sNewNode);
+ Sequence<PropertyValue> aValues(aNodeNames.getLength());
+ PropertyValue* pValues = aValues.getArray();
+ const OUString* pNodeNames = aNodeNames.getConstArray();
+ for(sal_Int32 i = 0; i < aNodeNames.getLength(); i++)
+ {
+ pValues[i].Name = "/" + pNodeNames[i];
+ }
+
+ pValues[0].Value <<= aDBData.sDataSource;
+ pValues[1].Value <<= aDBData.sCommand;
+ pValues[2].Value <<= aDBData.nCommandType;
+ pValues[3].Value <<= m_xEdDbText->get_text();
+
+ OUString sTmp;
+ const sal_Int32 nCnt = m_xLbTableCol->n_children();
+ for( sal_Int32 n = 0; n < nCnt; ++n )
+ sTmp += m_xLbTableCol->get_text(n) + "\x0a";
+
+ if (!sTmp.isEmpty())
+ pValues[4].Value <<= sTmp;
+
+ if( sNoTmpl != (sTmp = m_xLbDbParaColl->get_active_text()) )
+ pValues[5].Value <<= sTmp;
+
+ if (m_xTAutoFormat)
+ pValues[6].Value <<= m_xTAutoFormat->GetName();
+
+ pValues[7].Value <<= m_xRbAsTable->get_active();
+ pValues[8].Value <<= m_xRbAsField->get_active();
+ pValues[9].Value <<= m_xCbTableHeadon->get_active();
+ pValues[10].Value <<= m_xRbHeadlEmpty->get_active();
+
+ SetSetProperties(OUString(), aValues);
+
+ sNewNode += "/ColumnSet";
+
+ LanguageType ePrevLang(0xffff);
+
+ SvNumberFormatter& rNFormatr = *pView->GetWrtShell().GetNumberFormatter();
+ for(size_t nCol = 0; nCol < aDBColumns.size(); nCol++)
+ {
+ SwInsDBColumn* pColumn = aDBColumns[nCol].get();
+ OUString sColumnInsertNode(sNewNode + "/__");
+ if( nCol < 10 )
+ sColumnInsertNode += "00";
+ else if( nCol < 100 )
+ sColumnInsertNode += "0";
+ sColumnInsertNode += OUString::number( nCol );
+
+ const Sequence <OUString> aSubNodeNames = lcl_CreateSubNames(sColumnInsertNode);
+ Sequence<PropertyValue> aSubValues(aSubNodeNames.getLength());
+ PropertyValue* pSubValues = aSubValues.getArray();
+ sal_Int32 i = 0;
+
+ for( const OUString& rSubNodeName : aSubNodeNames)
+ pSubValues[i++].Name = rSubNodeName;
+ pSubValues[0].Value <<= pColumn->sColumn;
+ pSubValues[1].Value <<= i;
+ pSubValues[2].Value <<= pColumn->bHasFormat;
+ pSubValues[3].Value <<= pColumn->bIsDBFormat;
+
+ SwStyleNameMapper::FillUIName( RES_POOLCOLL_STANDARD, sTmp );
+ const SvNumberformat* pNF = rNFormatr.GetEntry( pColumn->nUsrNumFormat );
+ LanguageType eLang;
+ if( pNF )
+ {
+ pSubValues[4].Value <<= pNF->GetFormatstring();
+ eLang = pNF->GetLanguage();
+ }
+ else
+ {
+ pSubValues[4].Value <<= sTmp;
+ eLang = GetAppLanguage();
+ }
+
+ OUString sPrevLang;
+ if( eLang != ePrevLang )
+ {
+ sPrevLang = LanguageTag::convertToBcp47( eLang );
+ ePrevLang = eLang;
+ }
+
+ pSubValues[5].Value <<= sPrevLang;
+ SetSetProperties(sNewNode, aSubValues);
+ }
+}
+
+void SwInsertDBColAutoPilot::Load()
+{
+ const Sequence<OUString> aNames = GetNodeNames(OUString());
+ SvNumberFormatter& rNFormatr = *pView->GetWrtShell().GetNumberFormatter();
+ for(OUString const & nodeName : aNames)
+ {
+ //search for entries with the appropriate data source and table
+ Sequence<OUString> aSourceNames = lcl_createSourceNames(nodeName);
+
+ Sequence< Any> aDataSourceProps = GetProperties(aSourceNames);
+ const Any* pDataSourceProps = aDataSourceProps.getConstArray();
+ OUString sSource, sCommand;
+ sal_Int16 nCommandType;
+ pDataSourceProps[0] >>= sSource;
+ pDataSourceProps[1] >>= sCommand;
+ pDataSourceProps[2] >>= nCommandType;
+ if(sSource == aDBData.sDataSource && sCommand == aDBData.sCommand)
+ {
+ DB_ColumnConfigData aNewData;
+
+ pDataSourceProps[3] >>= aNewData.sEdit;
+ pDataSourceProps[4] >>= aNewData.sTableList;
+ pDataSourceProps[5] >>= aNewData.sTmplNm;
+ pDataSourceProps[6] >>= aNewData.sTAutoFormatNm;
+ if(pDataSourceProps[7].hasValue())
+ aNewData.bIsTable = *o3tl::doAccess<bool>(pDataSourceProps[7]);
+ if(pDataSourceProps[8].hasValue())
+ aNewData.bIsField = *o3tl::doAccess<bool>(pDataSourceProps[8]);
+ if(pDataSourceProps[9].hasValue())
+ aNewData.bIsHeadlineOn = *o3tl::doAccess<bool>(pDataSourceProps[9]);
+ if(pDataSourceProps[10].hasValue())
+ aNewData.bIsEmptyHeadln = *o3tl::doAccess<bool>(pDataSourceProps[10]);
+
+ const OUString sSubNodeName(nodeName + "/ColumnSet/");
+ const Sequence <OUString> aSubNames = GetNodeNames(sSubNodeName);
+ for(const OUString& rSubName : aSubNames)
+ {
+ Sequence <OUString> aSubNodeNames =
+ lcl_CreateSubNames(OUStringConcatenation(sSubNodeName + rSubName));
+ Sequence< Any> aSubProps = GetProperties(aSubNodeNames);
+ const Any* pSubProps = aSubProps.getConstArray();
+
+ OUString sColumn;
+ pSubProps[0] >>= sColumn;
+ // check for existence of the loaded column name
+ bool bFound = false;
+ for(size_t nRealColumn = 0; nRealColumn < aDBColumns.size(); ++nRealColumn)
+ {
+ if(aDBColumns[nRealColumn]->sColumn == sColumn)
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if(!bFound)
+ continue;
+ sal_Int16 nIndex = 0;
+ pSubProps[1] >>= nIndex;
+ std::unique_ptr<SwInsDBColumn> pInsDBColumn(new SwInsDBColumn(sColumn));
+ if(pSubProps[2].hasValue())
+ pInsDBColumn->bHasFormat = *o3tl::doAccess<bool>(pSubProps[2]);
+ if(pSubProps[3].hasValue())
+ pInsDBColumn->bIsDBFormat = *o3tl::doAccess<bool>(pSubProps[3]);
+
+ pSubProps[4] >>= pInsDBColumn->sUsrNumFormat;
+ OUString sNumberFormatLocale;
+ pSubProps[5] >>= sNumberFormatLocale;
+
+ /* XXX Earlier versions wrote a Country-Language string in
+ * SwInsertDBColAutoPilot::Commit() that here was read as
+ * Language-Country with 2 characters copied to language,
+ * 1 character separator and unconditionally 2 characters read
+ * as country. So for 'DE-de' and locales that have similar
+ * case-insensitive equal language/country combos that may have
+ * worked, for all others not. FIXME if you need to read old
+ * data that you were never able to read before. */
+ pInsDBColumn->eUsrNumFormatLng = LanguageTag::convertToLanguageType( sNumberFormatLocale );
+
+ pInsDBColumn->nUsrNumFormat = rNFormatr.GetEntryKey( pInsDBColumn->sUsrNumFormat,
+ pInsDBColumn->eUsrNumFormatLng );
+
+ aNewData.aDBColumns.insert(std::move(pInsDBColumn));
+ }
+ OUString sTmp( aNewData.sTableList );
+ if( !sTmp.isEmpty() )
+ {
+ sal_Int32 n = 0;
+ do {
+ const OUString sEntry( sTmp.getToken( 0, '\x0a', n ) );
+ //preselect column - if they still exist!
+ if (m_xLbTableDbColumn->find_text(sEntry) != -1)
+ {
+ m_xLbTableCol->append_text(sEntry);
+ m_xLbTableDbColumn->remove_text(sEntry);
+ }
+ } while( n>=0 );
+
+ if (!m_xLbTableDbColumn->n_children())
+ {
+ m_xIbDbcolAllTo->set_sensitive( false );
+ m_xIbDbcolOneTo->set_sensitive( false );
+ }
+ m_xIbDbcolOneFrom->set_sensitive(true);
+ m_xIbDbcolAllFrom->set_sensitive(true);
+ }
+ m_xEdDbText->set_text( aNewData.sEdit );
+
+ sTmp = aNewData.sTmplNm;
+ if( !sTmp.isEmpty() )
+ m_xLbDbParaColl->set_active_text(sTmp);
+ else
+ m_xLbDbParaColl->set_active(0);
+
+ m_xTAutoFormat.reset();
+ sTmp = aNewData.sTAutoFormatNm;
+ if( !sTmp.isEmpty() )
+ {
+ // then load the AutoFormat file and look for Autoformat first
+ SwTableAutoFormatTable aAutoFormatTable;
+ aAutoFormatTable.Load();
+ for( size_t nAutoFormat = aAutoFormatTable.size(); nAutoFormat; )
+ if( sTmp == aAutoFormatTable[ --nAutoFormat ].GetName() )
+ {
+ m_xTAutoFormat.reset(new SwTableAutoFormat(aAutoFormatTable[nAutoFormat]));
+ break;
+ }
+ }
+
+ m_xRbAsTable->set_active( aNewData.bIsTable );
+ m_xRbAsField->set_active( aNewData.bIsField );
+ m_xRbAsText->set_active( !aNewData.bIsTable && !aNewData.bIsField );
+
+ m_xCbTableHeadon->set_active( aNewData.bIsHeadlineOn );
+ m_xRbHeadlColnms->set_active( !aNewData.bIsEmptyHeadln );
+ m_xRbHeadlEmpty->set_active( aNewData.bIsEmptyHeadln );
+ HeaderHdl(*m_xCbTableHeadon);
+
+ // now copy the user defined Numberformat strings to the
+ // Shell. Then only these are available as ID
+ for( size_t n = 0; n < aDBColumns.size() ; ++n )
+ {
+ SwInsDBColumn& rSet = *aDBColumns[ n ];
+ for( size_t m = 0; m < aNewData.aDBColumns.size() ; ++m )
+ {
+ SwInsDBColumn& rGet = *aNewData.aDBColumns[ m ];
+ if(rGet.sColumn == rSet.sColumn)
+ {
+ if( rGet.bHasFormat && !rGet.bIsDBFormat )
+ {
+ rSet.bIsDBFormat = false;
+ rSet.nUsrNumFormat = rNFormatr.GetEntryKey( rGet.sUsrNumFormat,
+ rGet.eUsrNumFormatLng );
+ if( NUMBERFORMAT_ENTRY_NOT_FOUND == rSet.nUsrNumFormat )
+ {
+ sal_Int32 nCheckPos;
+ SvNumFormatType nType;
+ rNFormatr.PutEntry( rGet.sUsrNumFormat, nCheckPos, nType,
+ rSet.nUsrNumFormat, rGet.eUsrNumFormatLng );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // when the cursor is inside of a table, table must NEVER be selectable
+ if( !m_xRbAsTable->get_sensitive() && m_xRbAsTable->get_active() )
+ m_xRbAsField->set_active(true);
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/dbtablepreviewdialog.cxx b/sw/source/ui/dbui/dbtablepreviewdialog.cxx
new file mode 100644
index 000000000..cd1556776
--- /dev/null
+++ b/sw/source/ui/dbui/dbtablepreviewdialog.cxx
@@ -0,0 +1,118 @@
+/* -*- 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 "dbtablepreviewdialog.hxx"
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+class DBTablePreviewFrame
+ : public cppu::WeakImplHelper<lang::XEventListener>
+{
+private:
+ css::uno::Reference<css::frame::XFrame2> m_xFrame;
+
+ virtual void SAL_CALL disposing(const lang::EventObject& /*Source*/) override
+ {
+ m_xFrame.clear();
+ }
+
+public:
+ DBTablePreviewFrame(const css::uno::Reference<css::frame::XFrame2>& rFrame)
+ : m_xFrame(rFrame)
+ {
+ }
+
+ void cleanup()
+ {
+ if (m_xFrame.is())
+ {
+ m_xFrame->setComponent(nullptr, nullptr);
+ m_xFrame->dispose();
+ m_xFrame.clear();
+ }
+ }
+};
+
+SwDBTablePreviewDialog::SwDBTablePreviewDialog(weld::Window* pParent, uno::Sequence< beans::PropertyValue> const & rValues)
+ : SfxDialogController(pParent, "modules/swriter/ui/tablepreviewdialog.ui", "TablePreviewDialog")
+ , m_xDescriptionFI(m_xBuilder->weld_label("description"))
+ , m_xBeamerWIN(m_xBuilder->weld_container("beamer"))
+{
+ Size aSize(m_xBeamerWIN->get_approximate_digit_width() * 80,
+ m_xBeamerWIN->get_text_height() * 18);
+ m_xBeamerWIN->set_size_request(aSize.Width(), aSize.Height());
+
+ auto pValue = std::find_if(rValues.begin(), rValues.end(),
+ [](const beans::PropertyValue& rValue) { return rValue.Name == "Command"; });
+ if (pValue != rValues.end())
+ {
+ OUString sDescription = m_xDescriptionFI->get_label();
+ OUString sTemp;
+ pValue->Value >>= sTemp;
+ m_xDescriptionFI->set_label(sDescription.replaceFirst("%1", sTemp));
+ }
+
+ css::uno::Reference<css::frame::XFrame2> xFrame;
+ try
+ {
+ // create a frame wrapper for myself
+ xFrame = frame::Frame::create( comphelper::getProcessComponentContext() );
+ xFrame->initialize(m_xBeamerWIN->CreateChildFrame());
+ }
+ catch (uno::Exception const &)
+ {
+ xFrame.clear();
+ }
+ if (!xFrame.is())
+ return;
+
+ m_xFrameListener.set(new DBTablePreviewFrame(xFrame));
+ xFrame->addEventListener(m_xFrameListener);
+
+ util::URL aURL;
+ aURL.Complete = ".component:DB/DataSourceBrowser";
+ uno::Reference<frame::XDispatch> xD = xFrame->queryDispatch(aURL, "",
+ css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
+ if (xD.is())
+ {
+ xD->dispatch(aURL, rValues);
+ m_xBeamerWIN->show();
+ }
+}
+
+SwDBTablePreviewDialog::~SwDBTablePreviewDialog()
+{
+ if (m_xFrameListener)
+ {
+ m_xFrameListener->cleanup();
+ m_xFrameListener.clear();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/dbtablepreviewdialog.hxx b/sw/source/ui/dbui/dbtablepreviewdialog.hxx
new file mode 100644
index 000000000..99315fa01
--- /dev/null
+++ b/sw/source/ui/dbui/dbtablepreviewdialog.hxx
@@ -0,0 +1,46 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_DBTABLEPREVIEWDIALOG_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_DBTABLEPREVIEWDIALOG_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <com/sun/star/uno/Sequence.h>
+
+namespace com::sun::star{
+ namespace beans{ struct PropertyValue; }
+ namespace frame{ class XFrame2; }
+}
+
+class DBTablePreviewFrame;
+
+class SwDBTablePreviewDialog : public SfxDialogController
+{
+ std::unique_ptr<weld::Label> m_xDescriptionFI;
+ std::unique_ptr<weld::Container> m_xBeamerWIN;
+
+ rtl::Reference<DBTablePreviewFrame> m_xFrameListener;
+public:
+ SwDBTablePreviewDialog(weld::Window* pParent,
+ css::uno::Sequence< css::beans::PropertyValue> const & rValues );
+ virtual ~SwDBTablePreviewDialog() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mailmergewizard.cxx b/sw/source/ui/dbui/mailmergewizard.cxx
new file mode 100644
index 000000000..87116262f
--- /dev/null
+++ b/sw/source/ui/dbui/mailmergewizard.cxx
@@ -0,0 +1,265 @@
+/* -*- 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 <mailmergewizard.hxx>
+#include "mmdocselectpage.hxx"
+#include "mmoutputtypepage.hxx"
+#include "mmaddressblockpage.hxx"
+#include "mmgreetingspage.hxx"
+#include "mmlayoutpage.hxx"
+#include <mmconfigitem.hxx>
+#include <swabstdlg.hxx>
+#include <strings.hrc>
+#include <view.hxx>
+
+#include <helpids.h>
+
+using namespace svt;
+using namespace ::com::sun::star;
+
+SwMailMergeWizard::SwMailMergeWizard(SwView& rView, std::shared_ptr<SwMailMergeConfigItem> const & rItem)
+ : RoadmapWizardMachine(rView.GetFrameWeld())
+ , m_pSwView(&rView)
+ , m_bDocumentLoad(false)
+ , m_xConfigItem(rItem)
+ , m_sStarting(SwResId(ST_STARTING))
+ , m_sDocumentType(SwResId(ST_DOCUMENTTYPE))
+ , m_sAddressBlock(SwResId(ST_ADDRESSBLOCK))
+ , m_sAddressList(SwResId(ST_ADDRESSLIST))
+ , m_sGreetingsLine(SwResId(ST_GREETINGSLINE))
+ , m_sLayout(SwResId(ST_LAYOUT))
+ , m_nRestartPage(MM_DOCUMENTSELECTPAGE)
+{
+ defaultButton(WizardButtonFlags::NEXT);
+ enableButtons(WizardButtonFlags::FINISH, false);
+
+ setTitleBase(SwResId(ST_MMWTITLE));
+
+ m_xFinish->set_label(SwResId( ST_FINISH ));
+ m_xNextPage->set_help_id(HID_MM_NEXT_PAGE);
+ m_xPrevPage->set_help_id(HID_MM_PREV_PAGE);
+
+ //#i51949# no output type page visible if e-Mail is not supported
+ if (m_xConfigItem->IsMailAvailable())
+ declarePath(
+ 0,
+ {MM_DOCUMENTSELECTPAGE,
+ MM_OUTPUTTYPETPAGE,
+ MM_ADDRESSBLOCKPAGE,
+ MM_GREETINGSPAGE,
+ MM_LAYOUTPAGE}
+ );
+ else
+ declarePath(
+ 0,
+ {MM_DOCUMENTSELECTPAGE,
+ MM_ADDRESSBLOCKPAGE,
+ MM_GREETINGSPAGE,
+ MM_LAYOUTPAGE}
+ );
+
+ ActivatePage();
+ m_xAssistant->set_current_page(0);
+ UpdateRoadmap();
+}
+
+SwMailMergeWizard::~SwMailMergeWizard()
+{
+}
+
+std::unique_ptr<BuilderPage> SwMailMergeWizard::createPage(WizardState _nState)
+{
+ OString sIdent(OString::number(_nState));
+ weld::Container* pPageContainer = m_xAssistant->append_page(sIdent);
+
+ std::unique_ptr<vcl::OWizardPage> xRet;
+ switch(_nState)
+ {
+ case MM_DOCUMENTSELECTPAGE :
+ xRet = std::make_unique<SwMailMergeDocSelectPage>(pPageContainer, this);
+
+ /* tdf#52986 Set help ID using SetRoadmapHelpId for all pages
+ so that when by default the focus is on the left side pane of
+ the wizard the relevant help page is displayed when hitting
+ the Help / F1 button */
+ SetRoadmapHelpId("modules/swriter/ui/mmselectpage/MMSelectPage");
+ break;
+ case MM_OUTPUTTYPETPAGE :
+ xRet = std::make_unique<SwMailMergeOutputTypePage>(pPageContainer, this);
+ SetRoadmapHelpId("modules/swriter/ui/mmoutputtypepage/MMOutputTypePage");
+ break;
+ case MM_ADDRESSBLOCKPAGE :
+ xRet = std::make_unique<SwMailMergeAddressBlockPage>(pPageContainer, this);
+ SetRoadmapHelpId("modules/swriter/ui/mmaddressblockpage/MMAddressBlockPage");
+ break;
+ case MM_GREETINGSPAGE :
+ xRet = std::make_unique<SwMailMergeGreetingsPage>(pPageContainer, this);
+ SetRoadmapHelpId("modules/swriter/ui/mmsalutationpage/MMSalutationPage");
+ break;
+ case MM_LAYOUTPAGE :
+ xRet = std::make_unique<SwMailMergeLayoutPage>(pPageContainer, this);
+ SetRoadmapHelpId("modules/swriter/ui/mmlayoutpage/MMLayoutPage");
+ break;
+ }
+
+ m_xAssistant->set_page_title(sIdent, getStateDisplayName(_nState));
+
+ OSL_ENSURE(xRet, "no page created in ::createPage");
+ return xRet;
+}
+
+void SwMailMergeWizard::enterState( WizardState _nState )
+{
+ ::vcl::RoadmapWizardMachine::enterState( _nState );
+
+ if (m_xConfigItem->GetTargetView())
+ {
+ //close the dialog, remove the target view, show the source view
+ m_nRestartPage = _nState;
+ //set ResultSet back to start
+ m_xConfigItem->MoveResultSet(1);
+ m_xAssistant->response(RET_REMOVE_TARGET);
+ return;
+ }
+ bool bEnablePrev = true;
+ bool bEnableNext = true;
+ switch(_nState)
+ {
+ case MM_DOCUMENTSELECTPAGE:
+ {
+ bEnablePrev = false; // the first page
+
+ OUString sDataSourceName = GetSwView()->GetDataSourceName();
+ if(!sDataSourceName.isEmpty() &&
+ !SwView::IsDataSourceAvailable(sDataSourceName))
+ {
+ bEnableNext = false;
+ }
+ }
+ break;
+ case MM_ADDRESSBLOCKPAGE :
+ bEnableNext = m_xConfigItem->GetResultSet().is();
+ break;
+ case MM_LAYOUTPAGE:
+ bEnableNext = false; // the last page
+ break;
+ }
+ enableButtons( WizardButtonFlags::PREVIOUS, bEnablePrev);
+ enableButtons( WizardButtonFlags::NEXT, bEnableNext);
+
+ UpdateRoadmap();
+}
+
+OUString SwMailMergeWizard::getStateDisplayName( WizardState _nState ) const
+{
+ switch(_nState)
+ {
+ case MM_DOCUMENTSELECTPAGE:
+ return m_sStarting;
+ case MM_OUTPUTTYPETPAGE:
+ return m_sDocumentType;
+ case MM_ADDRESSBLOCKPAGE:
+ return m_xConfigItem->IsOutputToLetter() ?
+ m_sAddressBlock : m_sAddressList;
+ case MM_GREETINGSPAGE:
+ return m_sGreetingsLine;
+ case MM_LAYOUTPAGE:
+ return m_sLayout;
+ }
+ return OUString();
+}
+
+// enables/disables pages in the roadmap depending on the current page and state
+void SwMailMergeWizard::UpdateRoadmap()
+{
+/*
+ MM_DOCUMENTSELECTPAGE > inactive after the layoutpage
+ MM_OUTPUTTYPETPAGE : > inactive after the layoutpage
+ MM_ADDRESSBLOCKPAGE > inactive after the layoutpage
+ MM_GREETINGSPAGE > inactive after the layoutpage
+ MM_LAYOUTPAGE > inactive after the layoutpage
+ inactive if address block and greeting are switched off
+ or are already inserted into the source document
+*/
+
+ // enableState( <page id>, false );
+ const sal_uInt16 nCurPage = m_xAssistant->get_current_page();
+ BuilderPage* pCurPage = GetPage( nCurPage );
+ if(!pCurPage)
+ return;
+ bool bAddressFieldsConfigured = !m_xConfigItem->IsOutputToLetter() ||
+ !m_xConfigItem->IsAddressBlock() ||
+ m_xConfigItem->IsAddressFieldsAssigned();
+ bool bGreetingFieldsConfigured = !m_xConfigItem->IsGreetingLine(false) ||
+ !m_xConfigItem->IsIndividualGreeting(false) ||
+ m_xConfigItem->IsGreetingFieldsAssigned();
+
+ //#i97436# if a document has to be loaded then enable output type page only
+ m_bDocumentLoad = false;
+ bool bEnableOutputTypePage = (nCurPage != MM_DOCUMENTSELECTPAGE) ||
+ static_cast<vcl::OWizardPage*>(pCurPage)->commitPage( ::vcl::WizardTypes::eValidate );
+
+ // handle the Finish button
+ bool bCanFinish = !m_bDocumentLoad && bEnableOutputTypePage &&
+ m_xConfigItem->GetResultSet().is() &&
+ bAddressFieldsConfigured &&
+ bGreetingFieldsConfigured;
+ enableButtons(WizardButtonFlags::FINISH, (nCurPage != MM_DOCUMENTSELECTPAGE) && bCanFinish);
+
+ for(sal_uInt16 nPage = MM_DOCUMENTSELECTPAGE; nPage <= MM_LAYOUTPAGE; ++nPage)
+ {
+ bool bEnable = true;
+ switch(nPage)
+ {
+ case MM_DOCUMENTSELECTPAGE:
+ bEnable = true;
+ break;
+ case MM_OUTPUTTYPETPAGE:
+ bEnable = bEnableOutputTypePage;
+ break;
+ case MM_ADDRESSBLOCKPAGE:
+ bEnable = !m_bDocumentLoad && bEnableOutputTypePage;
+ // update page title for email vs letter
+ m_xAssistant->set_page_title(OString::number(MM_ADDRESSBLOCKPAGE), getStateDisplayName(MM_ADDRESSBLOCKPAGE));
+ break;
+ case MM_GREETINGSPAGE:
+ bEnable = !m_bDocumentLoad && bEnableOutputTypePage &&
+ m_xConfigItem->GetResultSet().is() &&
+ bAddressFieldsConfigured;
+ break;
+ case MM_LAYOUTPAGE:
+ bEnable = bCanFinish &&
+ ((m_xConfigItem->IsAddressBlock() && !m_xConfigItem->IsAddressInserted()) ||
+ (m_xConfigItem->IsGreetingLine(false) && !m_xConfigItem->IsGreetingInserted() ));
+ break;
+ }
+ enableState( nPage, bEnable );
+ }
+}
+
+short SwMailMergeWizard::run()
+{
+ OSL_FAIL("SwMailMergeWizard cannot be executed via Dialog::Execute!\n"
+ "It creates a thread (MailDispatcher instance) that will call"
+ "back to VCL apartment => deadlock!\n"
+ "Use Dialog::StartExecuteAsync to execute the dialog!" );
+ return RET_CANCEL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmaddressblockpage.cxx b/sw/source/ui/dbui/mmaddressblockpage.cxx
new file mode 100644
index 000000000..53e4165d1
--- /dev/null
+++ b/sw/source/ui/dbui/mmaddressblockpage.cxx
@@ -0,0 +1,1581 @@
+/* -*- 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 "mmaddressblockpage.hxx"
+#include <mailmergewizard.hxx>
+#include <swtypes.hxx>
+#include "addresslistdialog.hxx"
+#include <editeng/eeitem.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/grabbagitem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/transfer.hxx>
+#include <mmconfigitem.hxx>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/string.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <vector>
+#include <strings.hrc>
+#include <mmaddressblockpage.hrc>
+#include <helpids.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+
+SwMailMergeAddressBlockPage::SwMailMergeAddressBlockPage(weld::Container* pPage, SwMailMergeWizard* pWizard)
+ : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmaddressblockpage.ui", "MMAddressBlockPage")
+ , m_pWizard(pWizard)
+ , m_xAddressListPB(m_xBuilder->weld_button("addresslist"))
+ , m_xCurrentAddressFI(m_xBuilder->weld_label("currentaddress"))
+ , m_xStep2(m_xBuilder->weld_container("step2"))
+ , m_xStep3(m_xBuilder->weld_container("step3"))
+ , m_xStep4(m_xBuilder->weld_container("step4"))
+ , m_xSettingsFI(m_xBuilder->weld_label("settingsft"))
+ , m_xAddressCB(m_xBuilder->weld_check_button("address"))
+ , m_xSettingsPB(m_xBuilder->weld_button("settings"))
+ , m_xHideEmptyParagraphsCB(m_xBuilder->weld_check_button("hideempty"))
+ , m_xAssignPB(m_xBuilder->weld_button("assign"))
+ , m_xDocumentIndexFI(m_xBuilder->weld_label("documentindex"))
+ , m_xPrevSetIB(m_xBuilder->weld_button("prev"))
+ , m_xNextSetIB(m_xBuilder->weld_button("next"))
+ , m_xDifferentlist(m_xBuilder->weld_button("differentlist"))
+ , m_xSettings(new SwAddressPreview(m_xBuilder->weld_scrolled_window("settingspreviewwin", true)))
+ , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("addresspreviewwin", true)))
+ , m_xSettingsWIN(new weld::CustomWeld(*m_xBuilder, "settingspreview", *m_xSettings))
+ , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "addresspreview", *m_xPreview))
+{
+ m_xSettingsWIN->set_size_request(m_xDifferentlist->get_approximate_digit_width() * 40,
+ m_xDifferentlist->get_text_height() * 6);
+ m_xPreviewWIN->set_size_request(m_xDifferentlist->get_approximate_digit_width() * 44,
+ m_xDifferentlist->get_text_height() * 6);
+ m_sChangeAddress = m_xDifferentlist->get_label();
+ m_sDocument = m_xDocumentIndexFI->get_label();
+
+ m_sCurrentAddress = m_xCurrentAddressFI->get_label();
+ m_xAddressListPB->connect_clicked(LINK(this, SwMailMergeAddressBlockPage, AddressListHdl_Impl));
+ m_xSettingsPB->connect_clicked(LINK(this, SwMailMergeAddressBlockPage, SettingsHdl_Impl));
+ m_xAssignPB->connect_clicked(LINK(this, SwMailMergeAddressBlockPage, AssignHdl_Impl ));
+ m_xAddressCB->connect_toggled(LINK(this, SwMailMergeAddressBlockPage, AddressBlockHdl_Impl));
+ m_xSettings->SetSelectHdl(LINK(this, SwMailMergeAddressBlockPage, AddressBlockSelectHdl_Impl));
+ m_xHideEmptyParagraphsCB->connect_toggled(LINK(this, SwMailMergeAddressBlockPage, HideParagraphsHdl_Impl));
+
+ Link<weld::Button&,void> aLink = LINK(this, SwMailMergeAddressBlockPage, InsertDataHdl_Impl);
+ m_xPrevSetIB->connect_clicked(aLink);
+ m_xNextSetIB->connect_clicked(aLink);
+
+ // lock in preferred size including current address line
+ Size aSize1(m_xContainer->get_preferred_size());
+
+ OUString sOrigLabel = m_xAddressListPB->get_label();
+ m_xAddressListPB->set_label(m_sChangeAddress);
+ Size aSize2(m_xContainer->get_preferred_size());
+ m_xAddressListPB->set_label(sOrigLabel);
+
+ m_xCurrentAddressFI->hide();
+
+ m_xContainer->set_size_request(std::max(aSize1.Width(), aSize2.Width()),
+ std::max(aSize1.Height(), aSize2.Height()));
+}
+
+SwMailMergeAddressBlockPage::~SwMailMergeAddressBlockPage()
+{
+ m_xPreviewWIN.reset();
+ m_xSettingsWIN.reset();
+ m_xPreview.reset();
+ m_xSettings.reset();
+}
+
+bool SwMailMergeAddressBlockPage::canAdvance() const
+{
+ return m_pWizard->GetConfigItem().GetResultSet().is();
+}
+
+void SwMailMergeAddressBlockPage::Activate()
+{
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ bool bIsLetter = rConfigItem.IsOutputToLetter();
+
+ //no address block is created for e-Mail
+ m_xStep2->set_visible(bIsLetter);
+ m_xStep3->set_visible(bIsLetter);
+ m_xStep4->set_visible(bIsLetter);
+
+ if (!bIsLetter)
+ return;
+
+ m_xHideEmptyParagraphsCB->set_active( rConfigItem.IsHideEmptyParagraphs() );
+ m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", "1"));
+
+ m_xSettings->Clear();
+ const uno::Sequence< OUString> aBlocks =
+ m_pWizard->GetConfigItem().GetAddressBlocks();
+ for(const auto& rAddress : aBlocks)
+ m_xSettings->AddAddress(rAddress);
+ m_xSettings->SelectAddress(o3tl::narrowing<sal_uInt16>(rConfigItem.GetCurrentAddressBlockIndex()));
+ m_xAddressCB->set_active(rConfigItem.IsAddressBlock());
+ AddressBlockHdl_Impl(*m_xAddressCB);
+ m_xSettings->SetLayout(1, 2);
+ InsertDataHdl(nullptr);
+}
+
+bool SwMailMergeAddressBlockPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+{
+ return ::vcl::WizardTypes::eTravelForward != _eReason || m_pWizard->GetConfigItem().GetResultSet().is();
+}
+
+IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, AddressListHdl_Impl, weld::Button&, void)
+{
+ try
+ {
+ SwAddressListDialog aAddrDialog(this);
+ if (RET_OK == aAddrDialog.run())
+ {
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ rConfigItem.SetCurrentConnection(
+ aAddrDialog.GetSource(),
+ aAddrDialog.GetConnection(),
+ aAddrDialog.GetColumnsSupplier(),
+ aAddrDialog.GetDBData());
+ OUString sFilter = aAddrDialog.GetFilter();
+ rConfigItem.SetFilter( sFilter );
+ InsertDataHdl(nullptr);
+ GetWizard()->UpdateRoadmap();
+ GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE));
+ }
+ }
+ catch (const uno::Exception& e)
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pWizard->getDialog(),
+ VclMessageType::Warning, VclButtonsType::Ok, e.Message));
+ xBox->run();
+ }
+}
+
+IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, SettingsHdl_Impl, weld::Button&, void)
+{
+ SwSelectAddressBlockDialog aDlg(m_pWizard->getDialog(), m_pWizard->GetConfigItem());
+ SwMailMergeConfigItem& rConfig = m_pWizard->GetConfigItem();
+ aDlg.SetAddressBlocks(rConfig.GetAddressBlocks(), m_xSettings->GetSelectedAddress());
+ aDlg.SetSettings(rConfig.IsIncludeCountry(), rConfig.GetExcludeCountry());
+ if (aDlg.run() == RET_OK)
+ {
+ //the dialog provides the selected address at the first position!
+ const uno::Sequence< OUString> aBlocks = aDlg.GetAddressBlocks();
+ rConfig.SetAddressBlocks(aBlocks);
+ m_xSettings->Clear();
+ for(const auto& rAddress : aBlocks)
+ m_xSettings->AddAddress(rAddress);
+ m_xSettings->SelectAddress(0);
+ m_xSettings->Invalidate(); // #i40408
+ rConfig.SetCountrySettings(aDlg.IsIncludeCountry(), aDlg.GetCountry());
+ InsertDataHdl(nullptr);
+ }
+ GetWizard()->UpdateRoadmap();
+ GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE));
+}
+
+IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, AssignHdl_Impl, weld::Button&, void)
+{
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ const sal_uInt16 nSel = m_xSettings->GetSelectedAddress();
+ const uno::Sequence< OUString> aBlocks = rConfigItem.GetAddressBlocks();
+ SwAssignFieldsDialog aDlg(m_pWizard->getDialog(), m_pWizard->GetConfigItem(), aBlocks[nSel], true);
+ if(RET_OK == aDlg.run())
+ {
+ //preview update
+ InsertDataHdl(nullptr);
+ GetWizard()->UpdateRoadmap();
+ GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE));
+ }
+}
+
+void SwMailMergeAddressBlockPage::EnableAddressBlock(bool bAll, bool bSelective)
+{
+ m_xSettingsFI->set_sensitive(bAll);
+ m_xAddressCB->set_sensitive(bAll);
+ bSelective &= bAll;
+ m_xHideEmptyParagraphsCB->set_sensitive(bSelective);
+ m_xSettingsWIN->set_sensitive(bSelective);
+ m_xSettingsPB->set_sensitive(bSelective);
+ m_xStep3->set_sensitive(bSelective);
+ m_xStep4->set_sensitive(bSelective);
+}
+
+IMPL_LINK(SwMailMergeAddressBlockPage, AddressBlockHdl_Impl, weld::Toggleable&, rBox, void)
+{
+ EnableAddressBlock(rBox.get_sensitive(), rBox.get_active());
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ rConfigItem.SetAddressBlock(m_xAddressCB->get_active());
+ m_pWizard->UpdateRoadmap();
+ GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE));
+}
+
+IMPL_LINK_NOARG(SwMailMergeAddressBlockPage, AddressBlockSelectHdl_Impl, LinkParamNone*, void)
+{
+ const sal_uInt16 nSel = m_xSettings->GetSelectedAddress();
+ const uno::Sequence< OUString> aBlocks =
+ m_pWizard->GetConfigItem().GetAddressBlocks();
+ m_xPreview->SetAddress(SwAddressPreview::FillData(aBlocks[nSel],
+ m_pWizard->GetConfigItem()));
+ m_pWizard->GetConfigItem().SetCurrentAddressBlockIndex( nSel );
+ GetWizard()->UpdateRoadmap();
+ GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE));
+}
+
+IMPL_LINK(SwMailMergeAddressBlockPage, HideParagraphsHdl_Impl, weld::Toggleable&, rBox, void)
+{
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ rConfigItem.SetHideEmptyParagraphs(rBox.get_active());
+}
+
+void SwMailMergeAddressBlockPage::InsertDataHdl(const weld::Button* pButton)
+{
+ //if no pButton is given, the first set has to be pre-set
+ SwMailMergeConfigItem& rConfig = m_pWizard->GetConfigItem();
+ std::unique_ptr<weld::WaitObject> xWaitObj(new weld::WaitObject(m_pWizard->getDialog()));
+ if(!pButton)
+ {
+ rConfig.GetResultSet();
+ }
+ else
+ {
+ bool bNext = pButton == m_xNextSetIB.get();
+ sal_Int32 nPos = rConfig.GetResultSetPosition();
+ rConfig.MoveResultSet( bNext ? ++nPos : --nPos);
+ }
+ xWaitObj.reset();
+ sal_Int32 nPos = rConfig.GetResultSetPosition();
+ bool bEnable = true;
+ if(nPos < 1)
+ {
+ bEnable = false;
+ nPos = 1;
+ }
+ else
+ {
+ //if output type is letter
+ if (m_xSettings->IsVisible())
+ {
+ //Fill data into preview
+ const sal_uInt16 nSel = m_xSettings->GetSelectedAddress();
+ const uno::Sequence< OUString> aBlocks =
+ m_pWizard->GetConfigItem().GetAddressBlocks();
+ m_xPreview->SetAddress(SwAddressPreview::FillData(aBlocks[nSel], rConfig));
+ }
+ }
+ m_xPrevSetIB->set_sensitive(bEnable);
+ m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", OUString::number(nPos)));
+
+ GetWizard()->enableButtons(WizardButtonFlags::NEXT, GetWizard()->isStateEnabled(MM_GREETINGSPAGE));
+ bool bHasResultSet = rConfig.GetResultSet().is();
+ m_xCurrentAddressFI->set_visible(bHasResultSet);
+ if(bHasResultSet)
+ {
+ m_xCurrentAddressFI->set_label(m_sCurrentAddress.replaceFirst("%1", rConfig.GetCurrentDBData().sDataSource));
+ m_xAddressListPB->set_label(m_sChangeAddress);
+ }
+ EnableAddressBlock(bHasResultSet, m_xAddressCB->get_active());
+}
+
+IMPL_LINK(SwMailMergeAddressBlockPage, InsertDataHdl_Impl, weld::Button&, rButton, void)
+{
+ InsertDataHdl(&rButton);
+}
+
+SwSelectAddressBlockDialog::SwSelectAddressBlockDialog(weld::Window* pParent, SwMailMergeConfigItem& rConfig)
+ : SfxDialogController(pParent, "modules/swriter/ui/selectblockdialog.ui", "SelectBlockDialog")
+ , m_rConfig(rConfig)
+ , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin", true)))
+ , m_xNewPB(m_xBuilder->weld_button("new"))
+ , m_xCustomizePB(m_xBuilder->weld_button("edit"))
+ , m_xDeletePB(m_xBuilder->weld_button("delete"))
+ , m_xNeverRB(m_xBuilder->weld_radio_button("never"))
+ , m_xAlwaysRB(m_xBuilder->weld_radio_button("always"))
+ , m_xDependentRB(m_xBuilder->weld_radio_button("dependent"))
+ , m_xCountryED(m_xBuilder->weld_entry("country"))
+ , m_xPreviewWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreview))
+{
+ m_xPreviewWin->set_size_request(m_xCountryED->get_approximate_digit_width() * 45,
+ m_xCountryED->get_text_height() * 12);
+
+ Link<weld::Button&,void> aCustomizeHdl = LINK(this, SwSelectAddressBlockDialog, NewCustomizeHdl_Impl);
+ m_xNewPB->connect_clicked(aCustomizeHdl);
+ m_xCustomizePB->connect_clicked(aCustomizeHdl);
+
+ m_xDeletePB->connect_clicked(LINK(this, SwSelectAddressBlockDialog, DeleteHdl_Impl));
+
+ Link<weld::Toggleable&,void> aLk = LINK(this, SwSelectAddressBlockDialog, IncludeHdl_Impl);
+ m_xNeverRB->connect_toggled(aLk);
+ m_xAlwaysRB->connect_toggled(aLk);
+ m_xDependentRB->connect_toggled(aLk);
+ m_xPreview->SetLayout(2, 2);
+ m_xPreview->EnableScrollBar();
+}
+
+SwSelectAddressBlockDialog::~SwSelectAddressBlockDialog()
+{
+}
+
+void SwSelectAddressBlockDialog::SetAddressBlocks(const uno::Sequence< OUString>& rBlocks,
+ sal_uInt16 nSelectedAddress)
+{
+ m_aAddressBlocks = rBlocks;
+ for (const auto& rAddressBlock : std::as_const(m_aAddressBlocks))
+ m_xPreview->AddAddress(rAddressBlock);
+ m_xPreview->SelectAddress(nSelectedAddress);
+}
+
+// return the address blocks and put the selected one to the first position
+const uno::Sequence< OUString >& SwSelectAddressBlockDialog::GetAddressBlocks()
+{
+ //put the selected block to the first position
+ const sal_Int32 nSelect = static_cast<sal_Int32>(m_xPreview->GetSelectedAddress());
+ if(nSelect)
+ {
+ uno::Sequence< OUString >aTemp(m_aAddressBlocks.getLength());
+ auto it = aTemp.getArray();
+ *it = std::as_const(m_aAddressBlocks)[nSelect];
+ it = std::copy_n(std::cbegin(m_aAddressBlocks), nSelect - 1, std::next(it));
+ std::copy(std::next(std::cbegin(m_aAddressBlocks), nSelect + 1), std::cend(m_aAddressBlocks), it);
+ m_aAddressBlocks = aTemp;
+ }
+ return m_aAddressBlocks;
+}
+
+void SwSelectAddressBlockDialog::SetSettings(
+ bool bIsCountry, const OUString& rCountry)
+{
+ weld::RadioButton *pActive = m_xNeverRB.get();
+ if(bIsCountry)
+ {
+ pActive = !rCountry.isEmpty() ? m_xDependentRB.get() : m_xAlwaysRB.get();
+ m_xCountryED->set_text(rCountry);
+ }
+ pActive->set_active(true);
+ IncludeHdl_Impl(*pActive);
+ m_xDeletePB->set_sensitive(m_aAddressBlocks.getLength() > 1);
+}
+
+OUString SwSelectAddressBlockDialog::GetCountry() const
+{
+ if (m_xDependentRB->get_active())
+ return m_xCountryED->get_text();
+ return OUString();
+}
+
+IMPL_LINK(SwSelectAddressBlockDialog, DeleteHdl_Impl, weld::Button&, rButton, void)
+{
+ if (m_aAddressBlocks.getLength())
+ {
+ const sal_Int32 nSelected = static_cast<sal_Int32>(m_xPreview->GetSelectedAddress());
+ comphelper::removeElementAt(m_aAddressBlocks, nSelected);
+ if (m_aAddressBlocks.getLength() <= 1)
+ rButton.set_sensitive(false);
+ m_xPreview->RemoveSelectedAddress();
+ }
+}
+
+IMPL_LINK(SwSelectAddressBlockDialog, NewCustomizeHdl_Impl, weld::Button&, rButton, void)
+{
+ bool bCustomize = &rButton == m_xCustomizePB.get();
+ SwCustomizeAddressBlockDialog::DialogType nType = bCustomize ?
+ SwCustomizeAddressBlockDialog::ADDRESSBLOCK_EDIT :
+ SwCustomizeAddressBlockDialog::ADDRESSBLOCK_NEW;
+ std::unique_ptr<SwCustomizeAddressBlockDialog> xDlg(new SwCustomizeAddressBlockDialog(&rButton,
+ m_rConfig, nType));
+ if(bCustomize)
+ {
+ xDlg->SetAddress(m_aAddressBlocks[m_xPreview->GetSelectedAddress()]);
+ }
+ if (RET_OK != xDlg->run())
+ return;
+
+ const OUString sNew = xDlg->GetAddress();
+ if(bCustomize)
+ {
+ m_xPreview->ReplaceSelectedAddress(sNew);
+ m_aAddressBlocks.getArray()[m_xPreview->GetSelectedAddress()] = sNew;
+ }
+ else
+ {
+ m_xPreview->AddAddress(sNew);
+ m_aAddressBlocks.realloc(m_aAddressBlocks.getLength() + 1);
+ const sal_Int32 nSelect = m_aAddressBlocks.getLength() - 1;
+ m_aAddressBlocks.getArray()[nSelect] = sNew;
+ m_xPreview->SelectAddress(o3tl::narrowing<sal_uInt16>(nSelect));
+ }
+ m_xDeletePB->set_sensitive(m_aAddressBlocks.getLength() > 1);
+}
+
+IMPL_LINK_NOARG(SwSelectAddressBlockDialog, IncludeHdl_Impl, weld::Toggleable&, void)
+{
+ m_xCountryED->set_sensitive(m_xDependentRB->get_active());
+}
+
+#define USER_DATA_SALUTATION -1
+#define USER_DATA_PUNCTUATION -2
+#define USER_DATA_TEXT -3
+#define USER_DATA_NONE -4
+
+IMPL_LINK(SwCustomizeAddressBlockDialog, TextFilterHdl, OUString&, rTest, bool)
+{
+ rTest = m_aTextFilter.filter(rTest);
+ return true;
+}
+
+SwCustomizeAddressBlockDialog::SwCustomizeAddressBlockDialog(
+ weld::Widget* pParent, SwMailMergeConfigItem& rConfig, DialogType eType)
+ : SfxDialogController(pParent, "modules/swriter/ui/addressblockdialog.ui",
+ "AddressBlockDialog")
+ , m_aTextFilter("<>")
+ , m_rConfigItem(rConfig)
+ , m_eType(eType)
+ , m_aSelectionChangedIdle("SwCustomizeAddressBlockDialog m_aSelectionChangedIdle")
+ , m_xAddressElementsFT(m_xBuilder->weld_label("addressesft"))
+ , m_xAddressElementsLB(m_xBuilder->weld_tree_view("addresses"))
+ , m_xInsertFieldIB(m_xBuilder->weld_button("toaddr"))
+ , m_xRemoveFieldIB(m_xBuilder->weld_button("fromaddr"))
+ , m_xDragFT(m_xBuilder->weld_label("addressdestft"))
+ , m_xUpIB(m_xBuilder->weld_button("up"))
+ , m_xLeftIB(m_xBuilder->weld_button("left"))
+ , m_xRightIB(m_xBuilder->weld_button("right"))
+ , m_xDownIB(m_xBuilder->weld_button("down"))
+ , m_xFieldFT(m_xBuilder->weld_label("customft"))
+ , m_xFieldCB(m_xBuilder->weld_combo_box("custom"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+ , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin", true)))
+ , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "addrpreview", *m_xPreview))
+ , m_xDragED(new AddressMultiLineEdit(this))
+ , m_xDragWIN(new weld::CustomWeld(*m_xBuilder, "addressdest", *m_xDragED))
+{
+ m_aSelectionChangedIdle.SetInvokeHandler( LINK( this, SwCustomizeAddressBlockDialog, SelectionChangedIdleHdl ) );
+
+ Size aSize(m_xDragED->GetDrawingArea()->get_size_request());
+ m_xPreview->set_size_request(aSize.Width(), aSize.Height());
+
+ m_xFieldCB->connect_entry_insert_text(LINK(this, SwCustomizeAddressBlockDialog, TextFilterHdl));
+ m_xAddressElementsLB->set_size_request(-1, m_xAddressElementsLB->get_height_rows(16));
+
+ if( eType >= GREETING_FEMALE )
+ {
+ m_xFieldFT->show();
+ m_xFieldCB->show();
+ m_xAddressElementsLB->append(OUString::number(USER_DATA_SALUTATION), SwResId(ST_SALUTATION));
+ m_xAddressElementsLB->append(OUString::number(USER_DATA_PUNCTUATION), SwResId(ST_PUNCTUATION));
+ m_xAddressElementsLB->append(OUString::number(USER_DATA_TEXT), SwResId(ST_TEXT));
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RA_SALUTATION); ++i)
+ m_aSalutations.push_back(SwResId(RA_SALUTATION[i]));
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RA_PUNCTUATION); ++i)
+ m_aPunctuations.push_back(SwResId(RA_PUNCTUATION[i]));
+ m_xDragED->SetText(" ");
+ m_xDialog->set_title(SwResId(eType == GREETING_MALE ? ST_TITLE_MALE : ST_TITLE_FEMALE));
+ m_xAddressElementsFT->set_label(SwResId(ST_SALUTATIONELEMENTS));
+ m_xInsertFieldIB->set_tooltip_text(SwResId(ST_INSERTSALUTATIONFIELD));
+ m_xRemoveFieldIB->set_tooltip_text(SwResId(ST_REMOVESALUTATIONFIELD));
+ m_xDragFT->set_label(SwResId(ST_DRAGSALUTATION));
+ }
+ else
+ {
+ if (eType == ADDRESSBLOCK_EDIT)
+ m_xDialog->set_title(SwResId(ST_TITLE_EDIT));
+ m_xDragED->SetText("\n\n\n\n\n");
+ /* Set custom HIDs for swriter/01/mm_newaddblo.xhp */
+ m_xAddressElementsLB->set_help_id( HID_MM_ADDBLOCK_ELEMENTS );
+ m_xInsertFieldIB->set_help_id( HID_MM_ADDBLOCK_INSERT );
+ m_xRemoveFieldIB->set_help_id( HID_MM_ADDBLOCK_REMOVE );
+ m_xDragWIN->set_help_id( HID_MM_ADDBLOCK_DRAG );
+ m_xPreviewWIN->set_help_id( HID_MM_ADDBLOCK_PREVIEW );
+ m_xRightIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS );
+ m_xLeftIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS );
+ m_xDownIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS );
+ m_xUpIB->set_help_id( HID_MM_ADDBLOCK_MOVEBUTTONS );
+ }
+
+ const std::vector<std::pair<OUString, int>>& rHeaders = m_rConfigItem.GetDefaultAddressHeaders();
+ for (size_t i = 0; i < rHeaders.size(); ++i)
+ m_xAddressElementsLB->append(OUString::number(i), rHeaders[i].first);
+ m_xOK->connect_clicked(LINK(this, SwCustomizeAddressBlockDialog, OKHdl_Impl));
+ m_xAddressElementsLB->connect_changed(LINK(this, SwCustomizeAddressBlockDialog, ListBoxSelectHdl_Impl));
+ if (m_xAddressElementsLB->n_children())
+ m_xAddressElementsLB->select(0);
+ m_xDragED->SetModifyHdl(LINK(this, SwCustomizeAddressBlockDialog, EditModifyHdl_Impl));
+ m_xDragED->SetSelectionChangedHdl( LINK( this, SwCustomizeAddressBlockDialog, SelectionChangedHdl_Impl));
+ m_xFieldCB->connect_changed(LINK(this, SwCustomizeAddressBlockDialog, FieldChangeComboBoxHdl_Impl));
+ Link<weld::Button&,void> aImgButtonHdl = LINK(this, SwCustomizeAddressBlockDialog, ImageButtonHdl_Impl);
+ m_xInsertFieldIB->connect_clicked(aImgButtonHdl);
+ m_xRemoveFieldIB->connect_clicked(aImgButtonHdl);
+ m_xUpIB->connect_clicked(aImgButtonHdl);
+ m_xLeftIB->connect_clicked(aImgButtonHdl);
+ m_xRightIB->connect_clicked(aImgButtonHdl);
+ m_xDownIB->connect_clicked(aImgButtonHdl);
+ UpdateImageButtons_Impl();
+}
+
+bool SwCustomizeAddressBlockDialog::SetCursorLogicPosition(const Point& rPosition)
+{
+ return m_xDragED->SetCursorLogicPosition(rPosition);
+}
+
+void SwCustomizeAddressBlockDialog::UpdateFields()
+{
+ m_xDragED->UpdateFields();
+}
+
+SwCustomizeAddressBlockDialog::~SwCustomizeAddressBlockDialog()
+{
+ m_xDragED->EndDropTarget();
+}
+
+IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, OKHdl_Impl, weld::Button&, void)
+{
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(SwCustomizeAddressBlockDialog, ListBoxSelectHdl_Impl, weld::TreeView&, rBox, void)
+{
+ sal_Int32 nUserData = rBox.get_selected_id().toInt32();
+ // Check if the selected entry is already in the address and then forbid inserting
+ m_xInsertFieldIB->set_sensitive(nUserData >= 0 || !HasItem(nUserData));
+}
+
+IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, EditModifyHdl_Impl, AddressMultiLineEdit&, void)
+{
+ m_xPreview->SetAddress(SwAddressPreview::FillData(GetAddress(), m_rConfigItem));
+ UpdateImageButtons_Impl();
+}
+
+IMPL_LINK(SwCustomizeAddressBlockDialog, ImageButtonHdl_Impl, weld::Button&, rButton, void)
+{
+ if (m_xInsertFieldIB.get() == &rButton)
+ {
+ int nEntry = m_xAddressElementsLB->get_selected_index();
+ if (nEntry != -1)
+ {
+ m_xDragED->InsertNewEntry("<" + m_xAddressElementsLB->get_text(nEntry) + ">");
+ }
+ }
+ else if (m_xRemoveFieldIB.get() == &rButton)
+ {
+ m_xDragED->RemoveCurrentEntry();
+ }
+ else
+ {
+ MoveItemFlags nMove = MoveItemFlags::Down;
+ if (m_xUpIB.get() == &rButton)
+ nMove = MoveItemFlags::Up;
+ else if (m_xLeftIB.get() == &rButton)
+ nMove = MoveItemFlags::Left;
+ else if (m_xRightIB.get() == &rButton)
+ nMove = MoveItemFlags::Right;
+ m_xDragED->MoveCurrentItem(nMove);
+ }
+ UpdateImageButtons_Impl();
+}
+
+sal_Int32 SwCustomizeAddressBlockDialog::GetSelectedItem_Impl() const
+{
+ sal_Int32 nRet = USER_DATA_NONE;
+ const OUString sSelected = m_xDragED->GetCurrentItem();
+ if(!sSelected.isEmpty())
+ {
+ for (int i = 0, nEntryCount = m_xAddressElementsLB->n_children(); i < nEntryCount; ++i)
+ {
+ const OUString sEntry = m_xAddressElementsLB->get_text(i);
+ if( sEntry == sSelected.subView( 1, sSelected.getLength() - 2 ) )
+ {
+ nRet = m_xAddressElementsLB->get_id(i).toInt32();
+ break;
+ }
+ }
+ }
+ return nRet;
+}
+
+bool SwCustomizeAddressBlockDialog::HasItem(sal_Int32 nUserData)
+{
+ //get the entry from the ListBox
+ OUString sEntry;
+ for (int i = 0, nEntryCount = m_xAddressElementsLB->n_children(); i < nEntryCount; ++i)
+ {
+ if (m_xAddressElementsLB->get_id(i).toInt32() == nUserData)
+ {
+ sEntry = m_xAddressElementsLB->get_text(i);
+ break;
+ }
+ }
+ //search for this entry in the content
+ return m_xDragED->GetText().indexOf(OUStringConcatenation("<" + sEntry + ">")) >= 0;
+}
+
+IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, SelectionChangedIdleHdl, Timer*, void)
+{
+ // called in case the selection of the edit field changes.
+ // determine selection - if it's one of the editable fields then
+ // enable the related ComboBox and fill it
+
+ // don't trigger outself again
+ m_xDragED->SetSelectionChangedHdl(Link<bool, void>());
+
+ sal_Int32 nSelected = GetSelectedItem_Impl();
+ if (USER_DATA_NONE != nSelected)
+ m_xDragED->SelectCurrentItem();
+
+ if(m_xFieldCB->get_visible() && (USER_DATA_NONE != nSelected) && (nSelected < 0))
+ {
+ //search in ListBox if it's one of the first entries
+ OUString sSelect;
+ std::vector<OUString>* pVector = nullptr;
+ switch(nSelected) {
+ case USER_DATA_SALUTATION:
+ sSelect = m_sCurrentSalutation;
+ pVector = &m_aSalutations;
+ break;
+ case USER_DATA_PUNCTUATION:
+ sSelect = m_sCurrentPunctuation;
+ pVector = &m_aPunctuations;
+ break;
+ case USER_DATA_TEXT:
+ sSelect = m_sCurrentText;
+ break;
+ }
+ m_xFieldCB->clear();
+ if(pVector) {
+ for (const auto& rItem : *pVector)
+ m_xFieldCB->append_text(rItem);
+ }
+ m_xFieldCB->set_entry_text(sSelect);
+ m_xFieldCB->set_sensitive(true);
+ m_xFieldFT->set_sensitive(true);
+ }
+ else
+ {
+ m_xFieldCB->set_sensitive(false);
+ m_xFieldFT->set_sensitive(false);
+ }
+
+ UpdateImageButtons_Impl();
+ m_xDragED->SetSelectionChangedHdl( LINK( this, SwCustomizeAddressBlockDialog, SelectionChangedHdl_Impl));
+}
+
+IMPL_LINK(SwCustomizeAddressBlockDialog, SelectionChangedHdl_Impl, bool, bIdle, void)
+{
+ if (bIdle)
+ m_aSelectionChangedIdle.Start();
+ else
+ {
+ m_aSelectionChangedIdle.Stop();
+ SelectionChangedIdleHdl(nullptr);
+ }
+}
+
+IMPL_LINK_NOARG(SwCustomizeAddressBlockDialog, FieldChangeComboBoxHdl_Impl, weld::ComboBox&, void)
+{
+ //changing the field content changes the related members, too
+ sal_Int32 nSelected = GetSelectedItem_Impl();
+ const OUString sContent = m_xFieldCB->get_active_text();
+ switch(nSelected) {
+ case USER_DATA_SALUTATION:
+ m_sCurrentSalutation = sContent;
+ break;
+ case USER_DATA_PUNCTUATION:
+ m_sCurrentPunctuation = sContent;
+ break;
+ case USER_DATA_TEXT:
+ m_sCurrentText = sContent;
+ break;
+ }
+ UpdateImageButtons_Impl();
+ m_xPreview->SetAddress(GetAddress());
+ EditModifyHdl_Impl(*m_xDragED);
+}
+
+void SwCustomizeAddressBlockDialog::UpdateImageButtons_Impl()
+{
+ MoveItemFlags nMove = m_xDragED->IsCurrentItemMoveable();
+ m_xUpIB->set_sensitive( bool(nMove & MoveItemFlags::Up) );
+ m_xLeftIB->set_sensitive( bool(nMove & MoveItemFlags::Left) );
+ m_xRightIB->set_sensitive( bool(nMove & MoveItemFlags::Right) );
+ m_xDownIB->set_sensitive( bool(nMove & MoveItemFlags::Down) );
+ m_xRemoveFieldIB->set_sensitive(m_xDragED->HasCurrentItem());
+ int nEntry = m_xAddressElementsLB->get_selected_index();
+ m_xInsertFieldIB->set_sensitive( nEntry != -1 &&
+ (m_xAddressElementsLB->get_id(nEntry).toInt32() >= 0 || !m_xFieldCB->get_active_text().isEmpty()));
+}
+
+void SwCustomizeAddressBlockDialog::SetAddress(const OUString& rAddress)
+{
+ m_xDragED->SetText(rAddress);
+ UpdateImageButtons_Impl();
+ EditModifyHdl_Impl(*m_xDragED);
+}
+
+OUString SwCustomizeAddressBlockDialog::GetAddress() const
+{
+ OUString sAddress(m_xDragED->GetAddress());
+ //remove placeholders by the actual content
+ if (m_xFieldFT->get_visible())
+ {
+ for (int i = 0, nEntryCount = m_xAddressElementsLB->n_children(); i < nEntryCount; ++i)
+ {
+ const OUString sEntry = "<" + m_xAddressElementsLB->get_text(i) + ">";
+ sal_Int32 nUserData = m_xAddressElementsLB->get_id(i).toInt32();
+ switch(nUserData)
+ {
+ case USER_DATA_SALUTATION:
+ sAddress = sAddress.replaceFirst(sEntry, m_sCurrentSalutation);
+ break;
+ case USER_DATA_PUNCTUATION:
+ sAddress = sAddress.replaceFirst(sEntry, m_sCurrentPunctuation);
+ break;
+ case USER_DATA_TEXT:
+ sAddress = sAddress.replaceFirst(sEntry, m_sCurrentText);
+ break;
+ }
+ }
+ }
+ return sAddress;
+}
+
+namespace {
+
+struct SwAssignFragment
+{
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::ComboBox> m_xComboBox;
+ std::unique_ptr<weld::Label> m_xPreview;
+
+ SwAssignFragment(weld::Container* pGrid, int nLine)
+ : m_xBuilder(Application::CreateBuilder(pGrid, "modules/swriter/ui/assignfragment.ui"))
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xComboBox(m_xBuilder->weld_combo_box("combobox"))
+ , m_xPreview(m_xBuilder->weld_label("preview"))
+ {
+ m_xLabel->set_grid_left_attach(0);
+ m_xLabel->set_grid_top_attach(nLine);
+
+ m_xComboBox->set_grid_left_attach(1);
+ m_xComboBox->set_grid_top_attach(nLine);
+
+ m_xPreview->set_grid_left_attach(2);
+ m_xPreview->set_grid_top_attach(nLine);
+ }
+};
+
+}
+
+class SwAssignFieldsControl
+{
+ friend class SwAssignFieldsDialog;
+ std::unique_ptr<weld::ScrolledWindow> m_xVScroll;
+ std::unique_ptr<weld::Container> m_xGrid;
+
+ std::vector<SwAssignFragment> m_aFields;
+
+ SwMailMergeConfigItem* m_rConfigItem;
+
+ Link<LinkParamNone*,void> m_aModifyHdl;
+
+ DECL_LINK(MatchHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(GotFocusHdl_Impl, weld::Widget&, void);
+
+ void MakeVisible(const tools::Rectangle & rRect);
+public:
+ SwAssignFieldsControl(std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Container> xGrid);
+
+ void Init(SwAssignFieldsDialog* pDialog, SwMailMergeConfigItem& rConfigItem);
+ void SetModifyHdl(const Link<LinkParamNone*,void>& rModifyHdl)
+ {
+ m_aModifyHdl = rModifyHdl;
+ m_aModifyHdl.Call(nullptr);
+ }
+};
+
+SwAssignFieldsControl::SwAssignFieldsControl(std::unique_ptr<weld::ScrolledWindow> xWindow,
+ std::unique_ptr<weld::Container> xGrid)
+ : m_xVScroll(std::move(xWindow))
+ , m_xGrid(std::move(xGrid))
+ , m_rConfigItem(nullptr)
+{
+}
+
+void SwAssignFieldsControl::Init(SwAssignFieldsDialog* pDialog, SwMailMergeConfigItem& rConfigItem)
+{
+ m_rConfigItem = &rConfigItem;
+
+ //get the name of the default headers
+ const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders();
+ //get the actual data
+ uno::Reference< XColumnsSupplier > xColsSupp( rConfigItem.GetResultSet(), uno::UNO_QUERY);
+ //get the name of the actual columns
+ uno::Reference <XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr;
+ uno::Sequence< OUString > aFields;
+ if(xColAccess.is())
+ aFields = xColAccess->getElementNames();
+
+ //get the current assignment list
+ //each position in this sequence matches the position in the header array rHeaders
+ //if no assignment is available an empty sequence will be returned
+ uno::Sequence< OUString> aAssignments = rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() );
+ Link<weld::ComboBox&,void> aMatchHdl = LINK(this, SwAssignFieldsControl, MatchHdl_Impl);
+ Link<weld::Widget&,void> aFocusHdl = LINK(this, SwAssignFieldsControl, GotFocusHdl_Impl);
+
+ int nLabelWidth(0), nComboBoxWidth(0), nPreviewWidth(0);
+
+ //fill the controls
+ for (size_t i = 0; i < rHeaders.size(); ++i)
+ {
+ m_aFields.emplace_back(m_xGrid.get(), i);
+
+ const OUString rHeader = rHeaders[i].first;
+ weld::ComboBox& rNewLB = *m_aFields.back().m_xComboBox;
+ rNewLB.append_text(SwResId(SW_STR_NONE));
+ rNewLB.set_active(0);
+
+ for (const OUString& rField : std::as_const(aFields))
+ rNewLB.append_text(rField);
+ //select the ListBox
+ //if there is an assignment
+ if(o3tl::make_unsigned(aAssignments.getLength()) > i && !aAssignments[i].isEmpty())
+ rNewLB.set_active_text(aAssignments[i]);
+ else //otherwise the current column name may match one of the db columns
+ rNewLB.set_active_text(rHeader);
+
+ weld::Label& rNewText = *m_aFields.back().m_xLabel;
+ rNewText.set_label("<" + rHeader + ">");
+
+ weld::Label& rNewPreview = *m_aFields.back().m_xPreview;
+ //then the preview can be filled accordingly
+ if (xColAccess.is() && rNewLB.get_active() > 0 &&
+ xColAccess->hasByName(rNewLB.get_active_text()))
+ {
+ uno::Any aCol = xColAccess->getByName(rNewLB.get_active_text());
+ uno::Reference< XColumn > xColumn;
+ aCol >>= xColumn;
+ if(xColumn.is())
+ {
+ try
+ {
+ rNewPreview.set_label(xColumn->getString());
+ }
+ catch (const SQLException&)
+ {
+ }
+ }
+ }
+
+ if (i == 0)
+ {
+ auto nLineHeight = m_xGrid->get_preferred_size().Height();
+ m_xVScroll->set_size_request(m_xVScroll->get_approximate_digit_width() * 65,
+ nLineHeight * 6);
+ nComboBoxWidth = rNewLB.get_preferred_size().Width();
+ }
+
+ nLabelWidth = std::max<int>(nLabelWidth, rNewText.get_preferred_size().Width());
+ nPreviewWidth = std::max<int>(nPreviewWidth, rNewPreview.get_preferred_size().Width());
+
+ rNewLB.connect_changed(aMatchHdl);
+ rNewLB.connect_focus_in(aFocusHdl);
+ rNewText.show();
+ rNewLB.show();
+ rNewPreview.show();
+ }
+ pDialog->ConnectSizeGroups(nLabelWidth, nComboBoxWidth, nPreviewWidth);
+}
+
+void SwAssignFieldsControl::MakeVisible(const tools::Rectangle & rRect)
+{
+ //determine range of visible positions
+ auto nMinVisiblePos = m_xVScroll->vadjustment_get_value();
+ auto nMaxVisiblePos = nMinVisiblePos + m_xVScroll->vadjustment_get_page_size();
+ if (rRect.Top() < nMinVisiblePos || rRect.Bottom() > nMaxVisiblePos)
+ m_xVScroll->vadjustment_set_value(rRect.Top());
+}
+
+IMPL_LINK(SwAssignFieldsControl, MatchHdl_Impl, weld::ComboBox&, rBox, void)
+{
+ const OUString sColumn = rBox.get_active_text();
+ uno::Reference< XColumnsSupplier > xColsSupp( m_rConfigItem->GetResultSet(), uno::UNO_QUERY);
+ uno::Reference <XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr;
+ OUString sPreview;
+ if(xColAccess.is() && xColAccess->hasByName(sColumn))
+ {
+ uno::Any aCol = xColAccess->getByName(sColumn);
+ uno::Reference< XColumn > xColumn;
+ aCol >>= xColumn;
+ if(xColumn.is())
+ {
+ try
+ {
+ sPreview = xColumn->getString();
+ }
+ catch (const sdbc::SQLException&)
+ {
+ }
+ }
+ }
+ auto aLBIter = std::find_if(m_aFields.begin(), m_aFields.end(), [&rBox](const SwAssignFragment& rFragment){
+ return &rBox == rFragment.m_xComboBox.get(); });
+ if (aLBIter != m_aFields.end())
+ {
+ auto nIndex = static_cast<sal_Int32>(std::distance(m_aFields.begin(), aLBIter));
+ m_aFields[nIndex].m_xPreview->set_label(sPreview);
+ }
+ m_aModifyHdl.Call(nullptr);
+}
+
+IMPL_LINK(SwAssignFieldsControl, GotFocusHdl_Impl, weld::Widget&, rBox, void)
+{
+ int x, y, width, height;
+ rBox.get_extents_relative_to(*m_xGrid, x, y, width, height);
+ // the container has a border of 3 in the .ui
+ tools::Rectangle aRect(Point(x - 3, y - 3), Size(width + 6, height + 6));
+ MakeVisible(aRect);
+}
+
+SwAssignFieldsDialog::SwAssignFieldsDialog(
+ weld::Window* pParent, SwMailMergeConfigItem& rConfigItem,
+ const OUString& rPreview,
+ bool bIsAddressBlock)
+ : SfxDialogController(pParent, "modules/swriter/ui/assignfieldsdialog.ui", "AssignFieldsDialog")
+ , m_sNone(SwResId(SW_STR_NONE))
+ , m_rPreviewString(rPreview)
+ , m_rConfigItem(rConfigItem)
+ , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin", true)))
+ , m_xMatchingFI(m_xBuilder->weld_label("MATCHING_LABEL"))
+ , m_xAddressTitle(m_xBuilder->weld_label("addresselem"))
+ , m_xMatchTitle(m_xBuilder->weld_label("matchelem"))
+ , m_xPreviewTitle(m_xBuilder->weld_label("previewelem"))
+ , m_xPreviewFI(m_xBuilder->weld_label("PREVIEW_LABEL"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+ , m_xPreviewWin(new weld::CustomWeld(*m_xBuilder, "PREVIEW", *m_xPreview))
+ , m_xFieldsControl(new SwAssignFieldsControl(m_xBuilder->weld_scrolled_window("scroll"),
+ m_xBuilder->weld_container("FIELDS")))
+{
+ m_xPreviewWin->set_size_request(m_xMatchingFI->get_approximate_digit_width() * 45,
+ m_xMatchingFI->get_text_height() * 5);
+ m_xFieldsControl->Init(this, rConfigItem);
+
+ const OUString sMatchesTo( SwResId(ST_MATCHESTO) );
+ if (!bIsAddressBlock)
+ {
+ m_xPreviewFI->set_label(SwResId(ST_SALUTATIONPREVIEW));
+ m_xMatchingFI->set_label(SwResId(ST_SALUTATIONMATCHING));
+ m_xAddressTitle->set_label(SwResId(ST_SALUTATIONELEMENT));
+ }
+
+ m_xFieldsControl->SetModifyHdl(LINK(this, SwAssignFieldsDialog, AssignmentModifyHdl_Impl ));
+ m_xMatchingFI->set_label(m_xMatchingFI->get_label().replaceAll("%1", sMatchesTo));
+ m_xOK->connect_clicked(LINK(this, SwAssignFieldsDialog, OkHdl_Impl));
+}
+
+SwAssignFieldsDialog::~SwAssignFieldsDialog()
+{
+}
+
+uno::Sequence< OUString > SwAssignFieldsDialog::CreateAssignments()
+{
+ uno::Sequence< OUString > aAssignments(
+ m_rConfigItem.GetDefaultAddressHeaders().size());
+ OUString* pAssignments = aAssignments.getArray();
+ sal_Int32 nIndex = 0;
+ for (const auto& rLBItem : m_xFieldsControl->m_aFields)
+ {
+ const OUString sSelect = rLBItem.m_xComboBox->get_active_text();
+ pAssignments[nIndex] = (m_sNone != sSelect) ? sSelect : OUString();
+ ++nIndex;
+ }
+ return aAssignments;
+}
+
+IMPL_LINK_NOARG(SwAssignFieldsDialog, OkHdl_Impl, weld::Button&, void)
+{
+ m_rConfigItem.SetColumnAssignment(
+ m_rConfigItem.GetCurrentDBData(),
+ CreateAssignments() );
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SwAssignFieldsDialog, AssignmentModifyHdl_Impl, LinkParamNone*, void)
+{
+ uno::Sequence< OUString > aAssignments = CreateAssignments();
+ const OUString sPreview = SwAddressPreview::FillData(
+ m_rPreviewString, m_rConfigItem, &aAssignments);
+ m_xPreview->SetAddress(sPreview);
+}
+
+void SwAssignFieldsDialog::ConnectSizeGroups(int nLabelWidth, int nComboBoxWidth, int nPreviewWidth)
+{
+ m_xAddressTitle->set_size_request(nLabelWidth, -1);
+ m_xMatchTitle->set_size_request(nComboBoxWidth, -1);
+ m_xPreviewTitle->set_size_request(nPreviewWidth, -1);
+}
+
+namespace
+{
+ const EECharAttrib* FindCharAttrib(int nStartPosition, std::vector<EECharAttrib>& rAttribList)
+ {
+ for (auto it = rAttribList.rbegin(); it != rAttribList.rend(); ++it)
+ {
+ const auto& rTextAtr = *it;
+ if (rTextAtr.pAttr->Which() != EE_CHAR_GRABBAG)
+ continue;
+ if (rTextAtr.nStart <= nStartPosition && rTextAtr.nEnd >= nStartPosition)
+ {
+ return &rTextAtr;
+ }
+ }
+
+ return nullptr;
+ }
+}
+
+AddressMultiLineEdit::AddressMultiLineEdit(SwCustomizeAddressBlockDialog *pParent)
+ : m_pParentDialog(pParent)
+{
+}
+
+void AddressMultiLineEdit::EndDropTarget()
+{
+ if (m_xDropTarget.is())
+ {
+ m_xEditEngine->RemoveView(m_xEditView.get());
+ auto xRealDropTarget = GetDrawingArea()->get_drop_target();
+ uno::Reference<css::datatransfer::dnd::XDropTargetListener> xListener(m_xDropTarget, uno::UNO_QUERY);
+ xRealDropTarget->removeDropTargetListener(xListener);
+ m_xDropTarget.clear();
+ }
+}
+
+AddressMultiLineEdit::~AddressMultiLineEdit()
+{
+ assert(!m_xDropTarget.is());
+}
+
+void AddressMultiLineEdit::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(160, 60), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ WeldEditView::SetDrawingArea(pDrawingArea);
+}
+
+bool AddressMultiLineEdit::KeyInput(const KeyEvent& rKEvt)
+{
+ if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
+ return false; // we want default esc behaviour
+ if (rKEvt.GetCharCode())
+ return true; // handled
+ return WeldEditView::KeyInput(rKEvt);
+}
+
+bool AddressMultiLineEdit::Command(const CommandEvent& rCEvt)
+{
+ if (rCEvt.GetCommand() == CommandEventId::StartExtTextInput ||
+ rCEvt.GetCommand() == CommandEventId::EndExtTextInput ||
+ rCEvt.GetCommand() == CommandEventId::ExtTextInput)
+ {
+ return true;
+ }
+ return WeldEditView::Command(rCEvt);
+}
+
+bool AddressMultiLineEdit::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (rMEvt.GetClicks() >= 2)
+ return true; // handled
+ return WeldEditView::MouseButtonDown(rMEvt);
+}
+
+OUString AddressMultiLineEdit::GetText() const
+{
+ return m_xEditEngine->GetText();
+}
+
+void AddressMultiLineEdit::SetText( const OUString& rStr )
+{
+ m_xEditEngine->SetText(rStr);
+ //set attributes to all address tokens
+
+ sal_Int32 nSequence(0);
+ SfxGrabBagItem aProtectAttr(EE_CHAR_GRABBAG);
+ const sal_uInt32 nParaCount = m_xEditEngine->GetParagraphCount();
+ for(sal_uInt32 nPara = 0; nPara < nParaCount; ++nPara)
+ {
+ sal_Int32 nIndex = 0;
+ const OUString sPara = m_xEditEngine->GetText( nPara );
+ if (!sPara.isEmpty() && !sPara.endsWith(" "))
+ {
+ ESelection aPaM(nPara, sPara.getLength(), nPara, sPara.getLength());
+ m_xEditEngine->QuickInsertText(" ", aPaM);
+ }
+ for(;;)
+ {
+ const sal_Int32 nStart = sPara.indexOf( '<', nIndex );
+ if (nStart < 0)
+ break;
+ const sal_Int32 nEnd = sPara.indexOf( '>', nStart );
+ if (nEnd < 0)
+ break;
+ nIndex = nEnd;
+ SfxItemSet aSet(m_xEditEngine->GetEmptyItemSet());
+ // make each one different, so they are not collapsed together
+ // as one attribute
+ aProtectAttr.GetGrabBag()["Index"] <<= nSequence++;
+ aSet.Put(aProtectAttr);
+ m_xEditEngine->QuickSetAttribs(aSet, ESelection(nPara, nStart, nPara, nEnd + 1));
+ }
+
+ }
+ // add two empty paragraphs at the end
+ if(m_pParentDialog->m_eType == SwCustomizeAddressBlockDialog::ADDRESSBLOCK_NEW ||
+ m_pParentDialog->m_eType == SwCustomizeAddressBlockDialog::ADDRESSBLOCK_EDIT)
+ {
+ sal_Int32 nLastLen = m_xEditEngine->GetText(nParaCount - 1).getLength();
+ if(nLastLen)
+ {
+ int nPara = nParaCount ? nParaCount - 1 : 0;
+ ESelection aPaM(nPara, nLastLen, nPara, nLastLen);
+ m_xEditEngine->QuickInsertText("\n \n ", aPaM);
+ }
+ }
+
+ m_xEditView->SetSelection(ESelection(0, 0, 0, 0));
+}
+
+// Insert the new entry in front of the entry at the beginning of the selection
+void AddressMultiLineEdit::InsertNewEntry( const OUString& rStr )
+{
+ // insert new entry after current selected one.
+ ESelection aSelection = m_xEditView->GetSelection();
+ const sal_uInt32 nPara = aSelection.nStartPara;
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(nPara, aAttribList);
+
+ sal_Int32 nIndex = aSelection.nEndPara;
+ const EECharAttrib* pAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if(nullptr != pAttrib)
+ nIndex = pAttrib->nEnd;
+ InsertNewEntryAtPosition( rStr, nPara, nIndex );
+
+ // select the new entry
+ m_xEditEngine->GetCharAttribs(nPara, aAttribList);
+ pAttrib = FindCharAttrib(nIndex, aAttribList);
+ const sal_Int32 nEnd = pAttrib ? pAttrib->nEnd : nIndex;
+ ESelection aEntrySel(nPara, nIndex, nPara, nEnd);
+ m_xEditView->SetSelection(aEntrySel);
+ Invalidate();
+ m_aModifyLink.Call(*this);
+}
+
+void AddressMultiLineEdit::InsertNewEntryAtPosition( const OUString& rStr, sal_uLong nPara, sal_uInt16 nIndex )
+{
+ ESelection aInsertPos(nPara, nIndex, nPara, nIndex);
+ m_xEditEngine->QuickInsertText(rStr, aInsertPos);
+
+ //restore the attributes
+ SetText( GetAddress() );
+
+ //select the newly inserted/moved element
+ m_xEditView->SetSelection(aInsertPos);
+ m_aSelectionLink.Call(false);
+}
+
+void AddressMultiLineEdit::RemoveCurrentEntry()
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+
+ const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if(pBeginAttrib &&
+ (pBeginAttrib->nStart <= aSelection.nStartPos
+ && pBeginAttrib->nEnd >= aSelection.nEndPos))
+ {
+ const sal_uInt32 nPara = aSelection.nStartPara;
+ ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd);
+ m_xEditEngine->QuickInsertText(OUString(), aEntrySel);
+ //restore the attributes
+ SetText( GetAddress() );
+ m_aModifyLink.Call(*this);
+ }
+}
+
+void AddressMultiLineEdit::MoveCurrentItem(MoveItemFlags nMove)
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+
+ const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if(!pBeginAttrib ||
+ pBeginAttrib->nStart > aSelection.nStartPos ||
+ pBeginAttrib->nEnd < aSelection.nEndPos)
+ return;
+
+ //current item has been found
+ sal_Int32 nPara = aSelection.nStartPara;
+ sal_Int32 nIndex = pBeginAttrib->nStart;
+ ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd);
+ const OUString sCurrentItem = m_xEditEngine->GetText(aEntrySel);
+ m_xEditEngine->RemoveAttribs(aEntrySel, false, EE_CHAR_GRABBAG);
+ m_xEditEngine->QuickInsertText(OUString(), aEntrySel);
+ m_xEditEngine->GetCharAttribs(nPara, aAttribList);
+ switch (nMove)
+ {
+ case MoveItemFlags::Left :
+ if(nIndex)
+ {
+ //go left to find a predecessor or simple text
+ --nIndex;
+ const OUString sPara = m_xEditEngine->GetText( nPara );
+ sal_Int32 nSearchIndex = sPara.lastIndexOf( '>', nIndex+1 );
+ if( nSearchIndex != -1 && nSearchIndex == nIndex )
+ {
+ nSearchIndex = sPara.lastIndexOf( '<', nIndex );
+ if( nSearchIndex != -1 )
+ nIndex = nSearchIndex;
+ }
+ }
+ break;
+ case MoveItemFlags::Right:
+ {
+ //go right to find a successor or simple text
+ ++nIndex;
+ const EECharAttrib* pEndAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if(pEndAttrib && pEndAttrib->nEnd >= nIndex)
+ {
+ nIndex = pEndAttrib->nEnd;
+ }
+ }
+ break;
+ case MoveItemFlags::Up :
+ --nPara;
+ nIndex = 0;
+ break;
+ case MoveItemFlags::Down :
+ ++nPara;
+ nIndex = 0;
+ break;
+ default: break;
+ }
+ //add a new paragraph if there is none yet
+ if (nPara >= m_xEditEngine->GetParagraphCount())
+ {
+ auto nInsPara = nPara - 1;
+ auto nInsPos = m_xEditEngine->GetTextLen( nPara - 1 );
+ ESelection aTemp(nInsPara, nInsPos, nInsPara, nInsPos);
+ m_xEditEngine->QuickInsertText("\n", aTemp);
+ }
+ InsertNewEntryAtPosition( sCurrentItem, nPara, nIndex );
+
+ // select the new entry [#i40817]
+ m_xEditEngine->GetCharAttribs(nPara, aAttribList);
+ const EECharAttrib* pAttrib = FindCharAttrib(nIndex, aAttribList);
+ if (pAttrib)
+ aEntrySel = ESelection(nPara, nIndex, nPara, pAttrib->nEnd);
+ m_xEditView->SetSelection(aEntrySel);
+ Invalidate();
+ m_aModifyLink.Call(*this);
+}
+
+MoveItemFlags AddressMultiLineEdit::IsCurrentItemMoveable() const
+{
+ MoveItemFlags nRet = MoveItemFlags::NONE;
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+
+ const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if (pBeginAttrib &&
+ (pBeginAttrib->nStart <= aSelection.nStartPos
+ && pBeginAttrib->nEnd >= aSelection.nEndPos))
+ {
+ if (pBeginAttrib->nStart)
+ nRet |= MoveItemFlags::Left;
+ //if there is an entry it can always be move to the right and down
+ nRet |= MoveItemFlags::Right | MoveItemFlags::Down;
+ if (aSelection.nStartPara > 0)
+ nRet |= MoveItemFlags::Up;
+ }
+ return nRet;
+}
+
+bool AddressMultiLineEdit::HasCurrentItem() const
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+
+ const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ return (pBeginAttrib &&
+ (pBeginAttrib->nStart <= aSelection.nStartPos
+ && pBeginAttrib->nEnd >= aSelection.nEndPos));
+}
+
+OUString AddressMultiLineEdit::GetCurrentItem() const
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+
+ const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if (pBeginAttrib &&
+ (pBeginAttrib->nStart <= aSelection.nStartPos
+ && pBeginAttrib->nEnd >= aSelection.nEndPos))
+ {
+ const sal_uInt32 nPara = aSelection.nStartPara;
+ ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd);
+ return m_xEditEngine->GetText( aEntrySel );
+ }
+ return OUString();
+}
+
+void AddressMultiLineEdit::SelectCurrentItem()
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+
+ const EECharAttrib* pBeginAttrib = FindCharAttrib(aSelection.nStartPos, aAttribList);
+ if (pBeginAttrib &&
+ (pBeginAttrib->nStart <= aSelection.nStartPos
+ && pBeginAttrib->nEnd >= aSelection.nEndPos))
+ {
+ const sal_uInt32 nPara = aSelection.nStartPara;
+ ESelection aEntrySel(nPara, pBeginAttrib->nStart, nPara, pBeginAttrib->nEnd);
+ m_xEditView->SetSelection(aEntrySel);
+ Invalidate();
+ }
+}
+
+OUString AddressMultiLineEdit::GetAddress() const
+{
+ OUString sRet;
+ const sal_uInt32 nParaCount = m_xEditEngine->GetParagraphCount();
+ for(sal_uInt32 nPara = nParaCount; nPara; --nPara)
+ {
+ const OUString sPara = comphelper::string::stripEnd(m_xEditEngine->GetText(nPara - 1), ' ');
+ //don't add empty trailing paragraphs
+ if(!sRet.isEmpty() || !sPara.isEmpty())
+ {
+ sRet = sPara + sRet;
+ //insert the para break
+ if(nPara > 1)
+ sRet = "\n" + sRet;
+ }
+ }
+ return sRet;
+}
+
+void AddressMultiLineEdit::UpdateFields()
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ //restore the attributes
+ SetText( GetAddress() );
+
+ //reselect the element
+ m_xEditView->SetSelection(aSelection);
+ m_aSelectionLink.Call(false);
+}
+
+void AddressMultiLineEdit::EditViewSelectionChange()
+{
+ WeldEditView::EditViewSelectionChange();
+ m_aSelectionLink.Call(true);
+}
+
+namespace
+{
+ // sit between the tree as drag source and the editview as drop target and translate
+ // the tree dnd data to the simple string the editview wants
+ class DropTargetListener : public cppu::WeakImplHelper< css::datatransfer::dnd::XDropTargetListener,
+ css::datatransfer::dnd::XDropTarget >
+ {
+ private:
+ css::uno::Reference<css::datatransfer::dnd::XDropTarget> m_xRealDropTarget;
+ std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> m_aListeners;
+ SwCustomizeAddressBlockDialog* m_pParentDialog;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& ) override
+ {
+ m_xRealDropTarget.clear();
+ m_aListeners.clear();
+ }
+
+ // XDropTargetListener
+ virtual void SAL_CALL drop( const css::datatransfer::dnd::DropTargetDropEvent& dtde ) override
+ {
+ SolarMutexGuard aGuard;
+
+ auto aReplacement(dtde);
+
+ Point aMousePos(dtde.LocationX, dtde.LocationY);
+ bool bAllowed = m_pParentDialog->SetCursorLogicPosition(aMousePos);
+ if (bAllowed)
+ {
+ if (weld::TreeView* pTree = m_pParentDialog->get_drag_source())
+ {
+ int nEntry = pTree->get_selected_index();
+ if (nEntry != -1)
+ {
+ sal_Int32 nUserData = pTree->get_id(nEntry).toInt32();
+ //special entries can only be once in the address / greeting
+ if (nUserData >= 0 || !m_pParentDialog->HasItem(nUserData))
+ {
+ rtl::Reference<TransferDataContainer> xContainer = new TransferDataContainer;
+ xContainer->CopyString( "<" + pTree->get_text(nEntry) + ">" );
+
+ // replace what the treeview is offering with what ImpEditView::drop wants
+ aReplacement.Transferable = xContainer.get();
+ }
+ }
+ }
+ }
+
+ std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ for (auto const& listener : aListeners)
+ listener->drop(aReplacement);
+
+ if (bAllowed)
+ m_pParentDialog->UpdateFields();
+ }
+
+ virtual void SAL_CALL dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& dtdee ) override
+ {
+ auto aReplacement(dtdee);
+ // replace what the treeview is offering with what ImpEditView::dragEnter wants
+ aReplacement.SupportedDataFlavors.realloc(1);
+ SotExchange::GetFormatDataFlavor(SotClipboardFormatId::STRING, aReplacement.SupportedDataFlavors.getArray()[0]);
+
+ std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ for (auto const& listener : aListeners)
+ listener->dragEnter(aReplacement);
+ }
+
+ virtual void SAL_CALL dragExit( const css::datatransfer::dnd::DropTargetEvent& dte ) override
+ {
+ std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ for (auto const& listener : aListeners)
+ listener->dragExit( dte );
+ }
+
+ virtual void SAL_CALL dragOver( const css::datatransfer::dnd::DropTargetDragEvent& dtde ) override
+ {
+ std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ for (auto const& listener : aListeners)
+ listener->dragOver( dtde );
+ }
+
+ virtual void SAL_CALL dropActionChanged( const css::datatransfer::dnd::DropTargetDragEvent& dtde ) override
+ {
+ std::vector<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ for (auto const& listener : aListeners)
+ listener->dropActionChanged( dtde );
+ }
+
+ // XDropTarget
+ virtual void SAL_CALL addDropTargetListener(const css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>& xListener) override
+ {
+ m_aListeners.push_back(xListener);
+ }
+
+ virtual void SAL_CALL removeDropTargetListener(const css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>& xListener) override
+ {
+ m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), xListener), m_aListeners.end());
+ }
+
+ virtual sal_Bool SAL_CALL isActive() override
+ {
+ return m_xRealDropTarget->isActive();
+ }
+
+ virtual void SAL_CALL setActive(sal_Bool active) override
+ {
+ m_xRealDropTarget->setActive(active);
+ }
+
+ virtual sal_Int8 SAL_CALL getDefaultActions() override
+ {
+ return m_xRealDropTarget->getDefaultActions();
+ }
+
+ virtual void SAL_CALL setDefaultActions(sal_Int8 actions) override
+ {
+ m_xRealDropTarget->setDefaultActions(actions);
+ }
+
+ public:
+ DropTargetListener(css::uno::Reference<css::datatransfer::dnd::XDropTarget> xRealDropTarget,
+ SwCustomizeAddressBlockDialog* pParentDialog)
+ : m_xRealDropTarget(xRealDropTarget)
+ , m_pParentDialog(pParentDialog)
+ {
+ }
+ };
+}
+
+css::uno::Reference<css::datatransfer::dnd::XDropTarget> AddressMultiLineEdit::GetDropTarget()
+{
+ if (!m_xDropTarget.is())
+ {
+ auto xRealDropTarget = GetDrawingArea()->get_drop_target();
+ rtl::Reference<DropTargetListener> pProxy = new DropTargetListener(xRealDropTarget, m_pParentDialog);
+ xRealDropTarget->addDropTargetListener(pProxy);
+ m_xDropTarget = pProxy;
+ }
+ return m_xDropTarget;
+}
+
+bool AddressMultiLineEdit::SetCursorLogicPosition(const Point& rPosition)
+{
+ Point aMousePos = EditViewOutputDevice().PixelToLogic(rPosition);
+ m_xEditView->SetCursorLogicPosition(aMousePos, false, true);
+
+ ESelection aSelection = m_xEditView->GetSelection();
+ std::vector<EECharAttrib> aAttribList;
+ m_xEditEngine->GetCharAttribs(aSelection.nStartPara, aAttribList);
+ return FindCharAttrib(aSelection.nStartPos, aAttribList) == nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmaddressblockpage.hxx b/sw/source/ui/dbui/mmaddressblockpage.hxx
new file mode 100644
index 000000000..c32ad5713
--- /dev/null
+++ b/sw/source/ui/dbui/mmaddressblockpage.hxx
@@ -0,0 +1,284 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_MMADDRESSBLOCKPAGE_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_MMADDRESSBLOCKPAGE_HXX
+
+#include <svx/weldeditview.hxx>
+#include <vcl/wizardmachine.hxx>
+#include <mailmergehelper.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <vcl/textfilter.hxx>
+#include <svl/lstner.hxx>
+#include <vcl/idle.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+class SwMailMergeWizard;
+class SwMailMergeConfigItem;
+
+class SwMailMergeAddressBlockPage : public vcl::OWizardPage
+{
+ OUString m_sDocument;
+ OUString m_sCurrentAddress;
+ OUString m_sChangeAddress;
+
+ SwMailMergeWizard* m_pWizard;
+
+ std::unique_ptr<weld::Button> m_xAddressListPB;
+ std::unique_ptr<weld::Label> m_xCurrentAddressFI;
+
+ std::unique_ptr<weld::Container> m_xStep2;
+ std::unique_ptr<weld::Container> m_xStep3;
+ std::unique_ptr<weld::Container> m_xStep4;
+
+ std::unique_ptr<weld::Label> m_xSettingsFI;
+ std::unique_ptr<weld::CheckButton> m_xAddressCB;
+ std::unique_ptr<weld::Button> m_xSettingsPB;
+ std::unique_ptr<weld::CheckButton> m_xHideEmptyParagraphsCB;
+
+ std::unique_ptr<weld::Button> m_xAssignPB;
+
+ std::unique_ptr<weld::Label> m_xDocumentIndexFI;
+ std::unique_ptr<weld::Button> m_xPrevSetIB;
+ std::unique_ptr<weld::Button> m_xNextSetIB;
+
+ std::unique_ptr<weld::Button> m_xDifferentlist;
+
+ std::unique_ptr<SwAddressPreview> m_xSettings;
+ std::unique_ptr<SwAddressPreview> m_xPreview;
+ std::unique_ptr<weld::CustomWeld> m_xSettingsWIN;
+ std::unique_ptr<weld::CustomWeld> m_xPreviewWIN;
+
+ void InsertDataHdl(const weld::Button* pButton);
+
+ DECL_LINK(AddressListHdl_Impl, weld::Button&, void);
+ DECL_LINK(SettingsHdl_Impl, weld::Button&, void);
+ DECL_LINK(AssignHdl_Impl, weld::Button&, void);
+ DECL_LINK(AddressBlockHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(InsertDataHdl_Impl, weld::Button&, void);
+ DECL_LINK(AddressBlockSelectHdl_Impl, LinkParamNone*, void);
+ DECL_LINK(HideParagraphsHdl_Impl, weld::Toggleable&, void);
+
+ void EnableAddressBlock(bool bAll, bool bSelective);
+
+ virtual void Activate() override;
+ virtual bool commitPage( ::vcl::WizardTypes::CommitPageReason _eReason ) override;
+ virtual bool canAdvance() const override;
+
+public:
+ SwMailMergeAddressBlockPage(weld::Container* pPage, SwMailMergeWizard* pWizard);
+ virtual ~SwMailMergeAddressBlockPage() override;
+ SwMailMergeWizard* GetWizard() { return m_pWizard; }
+};
+
+class SwSelectAddressBlockDialog : public SfxDialogController
+{
+ css::uno::Sequence< OUString> m_aAddressBlocks;
+ SwMailMergeConfigItem& m_rConfig;
+
+ std::unique_ptr<SwAddressPreview> m_xPreview;
+ std::unique_ptr<weld::Button> m_xNewPB;
+ std::unique_ptr<weld::Button> m_xCustomizePB;
+ std::unique_ptr<weld::Button> m_xDeletePB;
+ std::unique_ptr<weld::RadioButton> m_xNeverRB;
+ std::unique_ptr<weld::RadioButton> m_xAlwaysRB;
+ std::unique_ptr<weld::RadioButton> m_xDependentRB;
+ std::unique_ptr<weld::Entry> m_xCountryED;
+ std::unique_ptr<weld::CustomWeld> m_xPreviewWin;
+
+ DECL_LINK(NewCustomizeHdl_Impl, weld::Button&, void);
+ DECL_LINK(DeleteHdl_Impl, weld::Button&, void);
+ DECL_LINK(IncludeHdl_Impl, weld::Toggleable&, void);
+
+public:
+ SwSelectAddressBlockDialog(weld::Window* pParent, SwMailMergeConfigItem& rConfig);
+ virtual ~SwSelectAddressBlockDialog() override;
+
+ void SetAddressBlocks(const css::uno::Sequence< OUString>& rBlocks,
+ sal_uInt16 nSelected);
+ const css::uno::Sequence< OUString>& GetAddressBlocks();
+
+ void SetSettings(bool bIsCountry, const OUString& sCountry);
+ bool IsIncludeCountry() const {return !m_xNeverRB->get_active();}
+ OUString GetCountry() const;
+};
+
+class SwCustomizeAddressBlockDialog;
+
+enum class MoveItemFlags {
+ NONE = 0,
+ Left = 1,
+ Right = 2,
+ Up = 4,
+ Down = 8,
+};
+namespace o3tl {
+ template<> struct typed_flags<MoveItemFlags> : is_typed_flags<MoveItemFlags, 0x0f> {};
+}
+
+class AddressMultiLineEdit;
+
+class AddressMultiLineEdit : public WeldEditView
+ , public SfxListener
+{
+ Link<bool,void> m_aSelectionLink;
+ Link<AddressMultiLineEdit&,void> m_aModifyLink;
+ SwCustomizeAddressBlockDialog* m_pParentDialog;
+
+ virtual void EditViewSelectionChange() override;
+ virtual css::uno::Reference<css::datatransfer::dnd::XDropTarget> GetDropTarget() override;
+
+ virtual bool KeyInput(const KeyEvent& rKEvt) override;
+ virtual bool Command(const CommandEvent& rCEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+
+public:
+ AddressMultiLineEdit(SwCustomizeAddressBlockDialog *pParent);
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ void EndDropTarget();
+ bool SetCursorLogicPosition(const Point& rPosition);
+ void UpdateFields();
+ virtual ~AddressMultiLineEdit() override;
+
+ void SetSelectionChangedHdl( const Link<bool,void>& rLink ) { m_aSelectionLink = rLink; }
+ void SetModifyHdl( const Link<AddressMultiLineEdit&,void>& rLink ) { m_aModifyLink = rLink; }
+
+ void SetText( const OUString& rStr );
+ OUString GetText() const;
+ OUString GetAddress() const;
+
+ void InsertNewEntry( const OUString& rStr );
+ void InsertNewEntryAtPosition( const OUString& rStr, sal_uLong nPara, sal_uInt16 nIndex );
+ void RemoveCurrentEntry();
+
+ void MoveCurrentItem(MoveItemFlags nMove);
+ MoveItemFlags IsCurrentItemMoveable() const;
+ bool HasCurrentItem() const;
+ OUString GetCurrentItem() const;
+ void SelectCurrentItem();
+};
+
+class SwCustomizeAddressBlockDialog : public SfxDialogController
+{
+ friend class AddressMultiLineEdit;
+public:
+ enum DialogType
+ {
+ ADDRESSBLOCK_NEW,
+ ADDRESSBLOCK_EDIT,
+ GREETING_FEMALE,
+ GREETING_MALE
+ };
+private:
+ TextFilter m_aTextFilter;
+
+ std::vector<OUString> m_aSalutations;
+ std::vector<OUString> m_aPunctuations;
+
+ OUString m_sCurrentSalutation;
+ OUString m_sCurrentPunctuation;
+ OUString m_sCurrentText;
+
+ SwMailMergeConfigItem& m_rConfigItem;
+ DialogType m_eType;
+
+ Idle m_aSelectionChangedIdle;
+
+ std::unique_ptr<weld::Label> m_xAddressElementsFT;
+ std::unique_ptr<weld::TreeView> m_xAddressElementsLB;
+ std::unique_ptr<weld::Button> m_xInsertFieldIB;
+ std::unique_ptr<weld::Button> m_xRemoveFieldIB;
+ std::unique_ptr<weld::Label> m_xDragFT;
+ std::unique_ptr<weld::Button> m_xUpIB;
+ std::unique_ptr<weld::Button> m_xLeftIB;
+ std::unique_ptr<weld::Button> m_xRightIB;
+ std::unique_ptr<weld::Button> m_xDownIB;
+ std::unique_ptr<weld::Label> m_xFieldFT;
+ std::unique_ptr<weld::ComboBox> m_xFieldCB;
+ std::unique_ptr<weld::Button> m_xOK;
+ std::unique_ptr<SwAddressPreview> m_xPreview;
+ std::unique_ptr<weld::CustomWeld> m_xPreviewWIN;
+ std::unique_ptr<AddressMultiLineEdit> m_xDragED;
+ std::unique_ptr<weld::CustomWeld> m_xDragWIN;
+
+ DECL_LINK(OKHdl_Impl, weld::Button&, void);
+ DECL_LINK(ListBoxSelectHdl_Impl, weld::TreeView&, void);
+ DECL_LINK(EditModifyHdl_Impl, AddressMultiLineEdit&, void);
+ DECL_LINK(ImageButtonHdl_Impl, weld::Button&, void);
+ DECL_LINK(SelectionChangedHdl_Impl, bool, void);
+ DECL_LINK(FieldChangeComboBoxHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(TextFilterHdl, OUString&, bool);
+ DECL_LINK(SelectionChangedIdleHdl, Timer*, void);
+
+ sal_Int32 GetSelectedItem_Impl() const;
+ void UpdateImageButtons_Impl();
+
+public:
+ SwCustomizeAddressBlockDialog(weld::Widget* pParent, SwMailMergeConfigItem& rConfig, DialogType);
+ virtual ~SwCustomizeAddressBlockDialog() override;
+
+ bool SetCursorLogicPosition(const Point& rPosition);
+ void UpdateFields();
+
+ // for dragging from the TreeViews, return the active source
+ weld::TreeView* get_drag_source() const { return m_xAddressElementsLB->get_drag_source(); }
+ bool HasItem(sal_Int32 nUserData);
+
+ void SetAddress(const OUString& rAddress);
+ OUString GetAddress() const;
+};
+
+class SwAssignFieldsControl;
+class SwAssignFieldsDialog : public SfxDialogController
+{
+ OUString m_sNone;
+ OUString m_rPreviewString;
+
+ SwMailMergeConfigItem& m_rConfigItem;
+
+ std::unique_ptr<SwAddressPreview> m_xPreview;
+ std::unique_ptr<weld::Label> m_xMatchingFI;
+ std::unique_ptr<weld::Label> m_xAddressTitle;
+ std::unique_ptr<weld::Label> m_xMatchTitle;
+ std::unique_ptr<weld::Label> m_xPreviewTitle;
+ std::unique_ptr<weld::Label> m_xPreviewFI;
+ std::unique_ptr<weld::Button> m_xOK;
+ std::unique_ptr<weld::CustomWeld> m_xPreviewWin;
+ std::unique_ptr<SwAssignFieldsControl> m_xFieldsControl;
+
+ std::unique_ptr<weld::SizeGroup> m_xLabelGroup;
+ std::unique_ptr<weld::SizeGroup> m_xComboGroup;
+ std::unique_ptr<weld::SizeGroup> m_xPreviewGroup;
+
+ css::uno::Sequence< OUString > CreateAssignments();
+ DECL_LINK(OkHdl_Impl, weld::Button&, void);
+ DECL_LINK(AssignmentModifyHdl_Impl, LinkParamNone*, void);
+
+public:
+ SwAssignFieldsDialog(weld::Window* pParent,
+ SwMailMergeConfigItem& rConfigItem,
+ const OUString& rPreview,
+ bool bIsAddressBlock);
+
+ void ConnectSizeGroups(int nLabelWidth, int nComboBoxWidth, int nPreviewWidth);
+
+ virtual ~SwAssignFieldsDialog() override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmdocselectpage.cxx b/sw/source/ui/dbui/mmdocselectpage.cxx
new file mode 100644
index 000000000..180a6d044
--- /dev/null
+++ b/sw/source/ui/dbui/mmdocselectpage.cxx
@@ -0,0 +1,226 @@
+/* -*- 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 <sfx2/filedlghelper.hxx>
+#include <sfx2/new.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/docfac.hxx>
+#include <view.hxx>
+#include <docsh.hxx>
+#include "mmdocselectpage.hxx"
+#include <mailmergewizard.hxx>
+#include <swabstdlg.hxx>
+#include <mmconfigitem.hxx>
+#include <swuiexp.hxx>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace svt;
+
+SwMailMergeDocSelectPage::SwMailMergeDocSelectPage(weld::Container* pPage, SwMailMergeWizard* pWizard)
+ : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmselectpage.ui", "MMSelectPage")
+ , m_pWizard(pWizard)
+ , m_xCurrentDocRB(m_xBuilder->weld_radio_button("currentdoc"))
+ , m_xNewDocRB(m_xBuilder->weld_radio_button("newdoc"))
+ , m_xLoadDocRB(m_xBuilder->weld_radio_button("loaddoc"))
+ , m_xLoadTemplateRB(m_xBuilder->weld_radio_button("template"))
+ , m_xRecentDocRB(m_xBuilder->weld_radio_button("recentdoc"))
+ , m_xBrowseDocPB(m_xBuilder->weld_button("browsedoc"))
+ , m_xBrowseTemplatePB(m_xBuilder->weld_button("browsetemplate"))
+ , m_xRecentDocLB(m_xBuilder->weld_combo_box("recentdoclb"))
+ , m_xDataSourceWarningFT(m_xBuilder->weld_label("datasourcewarning"))
+ , m_xExchangeDatabasePB(m_xBuilder->weld_button("exchangedatabase"))
+{
+ m_xDataSourceWarningFT->set_label_type(weld::LabelType::Warning);
+ m_xCurrentDocRB->set_active(true);
+ DocSelectHdl(*m_xNewDocRB);
+
+ Link<weld::Toggleable&,void> aDocSelectLink = LINK(this, SwMailMergeDocSelectPage, DocSelectHdl);
+ m_xCurrentDocRB->connect_toggled(aDocSelectLink);
+ m_xNewDocRB->connect_toggled(aDocSelectLink);
+ m_xLoadDocRB->connect_toggled(aDocSelectLink);
+ m_xLoadTemplateRB->connect_toggled(aDocSelectLink);
+ m_xRecentDocRB->connect_toggled(aDocSelectLink);
+
+ Link<weld::Button&,void> aFileSelectHdl = LINK(this, SwMailMergeDocSelectPage, FileSelectHdl);
+ m_xBrowseDocPB->connect_clicked(aFileSelectHdl);
+ m_xBrowseTemplatePB->connect_clicked(aFileSelectHdl);
+
+ Link<weld::Button&,void> aExchangeDatabaseHdl = LINK(this, SwMailMergeDocSelectPage, ExchangeDatabaseHdl);
+ m_xExchangeDatabasePB->connect_clicked(aExchangeDatabaseHdl);
+
+ const uno::Sequence< OUString >& rDocs =
+ m_pWizard->GetConfigItem().GetSavedDocuments();
+ for(const auto& rDoc : rDocs)
+ {
+ //insert in reverse order
+ m_xRecentDocLB->insert_text(0, rDoc);
+ }
+ if (!rDocs.hasElements())
+ m_xRecentDocRB->set_sensitive(false);
+ else
+ m_xRecentDocLB->set_active(0);
+}
+
+SwMailMergeDocSelectPage::~SwMailMergeDocSelectPage()
+{
+}
+
+IMPL_LINK_NOARG(SwMailMergeDocSelectPage, DocSelectHdl, weld::Toggleable&, void)
+{
+ m_xRecentDocLB->set_sensitive(m_xRecentDocRB->get_active());
+ m_pWizard->UpdateRoadmap();
+ OUString sDataSourceName = m_pWizard->GetSwView()->GetDataSourceName();
+
+ if(m_xCurrentDocRB->get_active() &&
+ !sDataSourceName.isEmpty() &&
+ !SwView::IsDataSourceAvailable(sDataSourceName))
+ {
+ m_xDataSourceWarningFT->show();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, false);
+ }
+ else
+ {
+ m_xDataSourceWarningFT->hide();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_OUTPUTTYPETPAGE));
+ }
+
+ if(m_xCurrentDocRB->get_active())
+ m_xExchangeDatabasePB->set_sensitive(true);
+ else
+ m_xExchangeDatabasePB->set_sensitive(false);
+}
+
+IMPL_LINK(SwMailMergeDocSelectPage, FileSelectHdl, weld::Button&, rButton, void)
+{
+ bool bTemplate = m_xBrowseTemplatePB.get() == &rButton;
+
+ if(bTemplate)
+ {
+ m_xLoadTemplateRB->set_active(true);
+ SfxNewFileDialog aNewFileDlg(m_pWizard->getDialog(), SfxNewFileDialogMode::NONE);
+ sal_uInt16 nRet = aNewFileDlg.run();
+ if(RET_TEMPLATE_LOAD == nRet)
+ bTemplate = false;
+ else if(RET_CANCEL != nRet)
+ m_sLoadTemplateName = aNewFileDlg.GetTemplateFileName();
+ }
+ else
+ m_xLoadDocRB->set_active(true);
+
+ if(!bTemplate)
+ {
+ sfx2::FileDialogHelper aDlgHelper(TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, m_pWizard->getDialog());
+ aDlgHelper.SetContext(sfx2::FileDialogHelper::WriterMailMerge);
+ Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
+
+ SfxObjectFactory &rFact = m_pWizard->GetSwView()->GetDocShell()->GetFactory();
+ SfxFilterMatcher aMatcher( rFact.GetFactoryName() );
+ SfxFilterMatcherIter aIter( aMatcher );
+ std::shared_ptr<const SfxFilter> pFlt = aIter.First();
+ while( pFlt )
+ {
+ if( pFlt && pFlt->IsAllowedAsTemplate() )
+ {
+ const OUString sWild = pFlt->GetWildcard().getGlob();
+ xFP->appendFilter( pFlt->GetUIName(), sWild );
+
+ // #i40125
+ if(pFlt->GetFilterFlags() & SfxFilterFlags::DEFAULT)
+ xFP->setCurrentFilter( pFlt->GetUIName() ) ;
+ }
+
+ pFlt = aIter.Next();
+ }
+
+ if( ERRCODE_NONE == aDlgHelper.Execute() )
+ {
+ m_sLoadFileName = xFP->getSelectedFiles().getConstArray()[0];
+ }
+ }
+ m_pWizard->UpdateRoadmap();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_OUTPUTTYPETPAGE));
+}
+
+IMPL_LINK_NOARG(SwMailMergeDocSelectPage, ExchangeDatabaseHdl, weld::Button&, void)
+{
+
+ SwAbstractDialogFactory& rFact = ::swui::GetFactory();
+ ScopedVclPtr<VclAbstractDialog> pDlg(rFact.CreateSwChangeDBDlg(*m_pWizard->GetSwView()));
+ pDlg->Execute();
+
+ OUString sDataSourceName = m_pWizard->GetSwView()->GetDataSourceName();
+
+ if(m_xCurrentDocRB->get_active() &&
+ !sDataSourceName.isEmpty() &&
+ SwView::IsDataSourceAvailable(sDataSourceName))
+ {
+ m_xDataSourceWarningFT->hide();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, true);
+ }
+}
+
+bool SwMailMergeDocSelectPage::commitPage( ::vcl::WizardTypes::CommitPageReason _eReason )
+{
+ bool bReturn = false;
+ bool bNext = _eReason == ::vcl::WizardTypes::eTravelForward;
+ if(bNext || _eReason == ::vcl::WizardTypes::eValidate )
+ {
+ OUString sReloadDocument;
+ bReturn = m_xCurrentDocRB->get_active() ||
+ m_xNewDocRB->get_active();
+ if (!bReturn)
+ {
+ sReloadDocument = m_sLoadFileName;
+ bReturn = !sReloadDocument.isEmpty() && m_xLoadDocRB->get_active();
+ }
+ if (!bReturn)
+ {
+ sReloadDocument = m_sLoadTemplateName;
+ bReturn = !sReloadDocument.isEmpty() && m_xLoadTemplateRB->get_active();
+ }
+ if (!bReturn)
+ {
+ bReturn = m_xRecentDocRB->get_active();
+ if (bReturn)
+ {
+ sReloadDocument = m_xRecentDocLB->get_active_text();
+ bReturn = !sReloadDocument.isEmpty();
+ }
+ }
+ if( _eReason == ::vcl::WizardTypes::eValidate )
+ m_pWizard->SetDocumentLoad(!m_xCurrentDocRB->get_active());
+
+ if(bNext && !m_xCurrentDocRB->get_active())
+ {
+ if(!sReloadDocument.isEmpty())
+ m_pWizard->SetReloadDocument( sReloadDocument );
+ m_pWizard->SetRestartPage(MM_OUTPUTTYPETPAGE);
+ m_pWizard->response(RET_LOAD_DOC);
+ }
+ }
+ return bReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmdocselectpage.hxx b/sw/source/ui/dbui/mmdocselectpage.hxx
new file mode 100644
index 000000000..d1cdca67d
--- /dev/null
+++ b/sw/source/ui/dbui/mmdocselectpage.hxx
@@ -0,0 +1,58 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_MMDOCSELECTPAGE_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_MMDOCSELECTPAGE_HXX
+
+#include <vcl/wizardmachine.hxx>
+#include <vcl/weld.hxx>
+
+class SwMailMergeWizard;
+
+class SwMailMergeDocSelectPage : public vcl::OWizardPage
+{
+ OUString m_sLoadFileName;
+ OUString m_sLoadTemplateName;
+
+ SwMailMergeWizard* m_pWizard;
+
+ std::unique_ptr<weld::RadioButton> m_xCurrentDocRB;
+ std::unique_ptr<weld::RadioButton> m_xNewDocRB;
+ std::unique_ptr<weld::RadioButton> m_xLoadDocRB;
+ std::unique_ptr<weld::RadioButton> m_xLoadTemplateRB;
+ std::unique_ptr<weld::RadioButton> m_xRecentDocRB;
+ std::unique_ptr<weld::Button> m_xBrowseDocPB;
+ std::unique_ptr<weld::Button> m_xBrowseTemplatePB;
+ std::unique_ptr<weld::ComboBox> m_xRecentDocLB;
+ std::unique_ptr<weld::Label> m_xDataSourceWarningFT;
+ std::unique_ptr<weld::Button> m_xExchangeDatabasePB;
+
+ DECL_LINK(DocSelectHdl, weld::Toggleable&, void);
+ DECL_LINK(FileSelectHdl, weld::Button&, void);
+ DECL_LINK(ExchangeDatabaseHdl, weld::Button&, void);
+
+ virtual bool commitPage(::vcl::WizardTypes::CommitPageReason _eReason) override;
+
+public:
+ SwMailMergeDocSelectPage(weld::Container* pPage, SwMailMergeWizard* pWizard);
+ virtual ~SwMailMergeDocSelectPage() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmgreetingspage.cxx b/sw/source/ui/dbui/mmgreetingspage.cxx
new file mode 100644
index 000000000..13f234a35
--- /dev/null
+++ b/sw/source/ui/dbui/mmgreetingspage.cxx
@@ -0,0 +1,422 @@
+/* -*- 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 "mmgreetingspage.hxx"
+#include <mailmergewizard.hxx>
+#include <mmconfigitem.hxx>
+#include "mmaddressblockpage.hxx"
+#include <dbui.hrc>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <tools/diagnose_ex.h>
+#include <swmodule.hxx>
+#include <view.hxx>
+
+using namespace svt;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+static void lcl_FillGreetingsBox(weld::ComboBox& rBox,
+ SwMailMergeConfigItem const & rConfig,
+ SwMailMergeConfigItem::Gender eType)
+{
+ const Sequence< OUString> rEntries = rConfig.GetGreetings(eType);
+ for(const auto& rEntry : rEntries)
+ rBox.append_text(rEntry);
+ rBox.set_active(rConfig.GetCurrentGreeting(eType));
+}
+
+static void lcl_StoreGreetingsBox(const weld::ComboBox& rBox,
+ SwMailMergeConfigItem& rConfig,
+ SwMailMergeConfigItem::Gender eType)
+{
+ Sequence< OUString> aEntries(rBox.get_count());
+ OUString* pEntries = aEntries.getArray();
+ for(sal_Int32 nEntry = 0; nEntry < rBox.get_count(); ++nEntry)
+ pEntries[nEntry] = rBox.get_text(nEntry);
+ rConfig.SetGreetings(eType, aEntries);
+ rConfig.SetCurrentGreeting(eType, rBox.get_active());
+}
+
+IMPL_LINK_NOARG(SwGreetingsHandler, IndividualHdl_Impl, weld::Toggleable&, void)
+{
+ bool bIndividual = m_xPersonalizedCB->get_sensitive() && m_xPersonalizedCB->get_active();
+ m_xFemaleFT->set_sensitive(bIndividual);
+ m_xFemaleLB->set_sensitive(bIndividual);
+ m_xFemalePB->set_sensitive(bIndividual);
+ m_xMaleFT->set_sensitive(bIndividual);
+ m_xMaleLB->set_sensitive(bIndividual);
+ m_xMalePB->set_sensitive(bIndividual);
+ m_xFemaleFI->set_sensitive(bIndividual);
+ m_xFemaleColumnFT->set_sensitive(bIndividual);
+ m_xFemaleColumnLB->set_sensitive(bIndividual);
+ m_xFemaleFieldFT->set_sensitive(bIndividual);
+ m_xFemaleFieldCB->set_sensitive(bIndividual);
+
+ if( m_bIsTabPage )
+ {
+ m_rConfigItem.SetIndividualGreeting(bIndividual, false);
+ m_pWizard->UpdateRoadmap();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE));
+ }
+ UpdatePreview();
+}
+
+IMPL_LINK(SwGreetingsHandler, GreetingHdl_Impl, weld::Button&, rButton, void)
+{
+ std::unique_ptr<SwCustomizeAddressBlockDialog> xDlg(new SwCustomizeAddressBlockDialog(&rButton, m_rConfigItem,
+ &rButton == m_xMalePB.get() ?
+ SwCustomizeAddressBlockDialog::GREETING_MALE :
+ SwCustomizeAddressBlockDialog::GREETING_FEMALE ));
+ if (RET_OK == xDlg->run())
+ {
+ weld::ComboBox* pToInsert = &rButton == m_xMalePB.get() ? m_xMaleLB.get() : m_xFemaleLB.get();
+ pToInsert->append_text(xDlg->GetAddress());
+ pToInsert->set_active(pToInsert->get_count() - 1);
+ if(m_bIsTabPage)
+ {
+ m_pWizard->UpdateRoadmap();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE));
+ }
+ UpdatePreview();
+ }
+}
+
+void SwGreetingsHandler::UpdatePreview()
+{
+ //the base class does nothing
+}
+
+IMPL_LINK_NOARG(SwMailMergeGreetingsPage, AssignHdl_Impl, weld::Button&, void)
+{
+ const OUString sPreview(m_xFemaleLB->get_active_text() + "\n" + m_xMaleLB->get_active_text());
+ SwAssignFieldsDialog aDlg(m_pWizard->getDialog(), m_rConfigItem, sPreview, false);
+ if (RET_OK == aDlg.run())
+ {
+ UpdatePreview();
+ m_pWizard->UpdateRoadmap();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE));
+ }
+}
+
+IMPL_LINK_NOARG(SwMailMergeGreetingsPage, GreetingSelectListBoxHdl_Impl, weld::ComboBox&, void)
+{
+ UpdatePreview();
+}
+
+IMPL_LINK_NOARG(SwMailMergeGreetingsPage, GreetingSelectComboBoxHdl_Impl, weld::ComboBox&, void)
+{
+ UpdatePreview();
+}
+
+void SwMailMergeGreetingsPage::UpdatePreview()
+{
+ //find out which type of greeting should be selected:
+ bool bFemale = false;
+ bool bNoValue = !m_xFemaleColumnLB->get_sensitive();
+ if( !bNoValue )
+ {
+ const OUString sFemaleValue = m_xFemaleFieldCB->get_active_text();
+ const OUString sFemaleColumn = m_xFemaleColumnLB->get_active_text();
+ Reference< sdbcx::XColumnsSupplier > xColsSupp( m_rConfigItem.GetResultSet(), UNO_QUERY);
+ Reference < container::XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr;
+ if(!sFemaleValue.isEmpty() && !sFemaleColumn.isEmpty() &&
+ xColAccess.is() &&
+ xColAccess->hasByName(sFemaleColumn))
+ {
+ //get the content and exchange it in the address string
+ Any aCol = xColAccess->getByName(sFemaleColumn);
+ Reference< sdb::XColumn > xColumn;
+ aCol >>= xColumn;
+ if(xColumn.is())
+ {
+ try
+ {
+ bFemale = xColumn->getString() == sFemaleValue;
+
+ //no last name value marks the greeting also as neutral
+ const OUString sLastNameColumn =
+ m_rConfigItem.GetAssignedColumn(MM_PART_LASTNAME);
+ if ( xColAccess->hasByName(sLastNameColumn) )
+ {
+ aCol = xColAccess->getByName(sLastNameColumn);
+ aCol >>= xColumn;
+ bNoValue = xColumn->getString().isEmpty();
+ }
+ }
+ catch (const sdbc::SQLException&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "");
+ }
+ }
+ }
+ }
+
+ OUString sPreview = bFemale ? m_xFemaleLB->get_active_text() :
+ bNoValue ? m_xNeutralCB->get_active_text() : m_xMaleLB->get_active_text();
+
+ sPreview = SwAddressPreview::FillData(sPreview, m_rConfigItem);
+ m_xPreview->SetAddress(sPreview);
+}
+
+void SwGreetingsHandler::Contains(bool bContainsGreeting)
+{
+ m_xPersonalizedCB->set_sensitive(bContainsGreeting);
+ bool bEnablePersonal = bContainsGreeting && m_xPersonalizedCB->get_active();
+ m_xFemaleFT->set_sensitive(bEnablePersonal);
+ m_xFemaleLB->set_sensitive(bEnablePersonal);
+ m_xFemalePB->set_sensitive(bEnablePersonal);
+ m_xMaleFT->set_sensitive(bEnablePersonal);
+ m_xMaleLB->set_sensitive(bEnablePersonal);
+ m_xMalePB->set_sensitive(bEnablePersonal);
+ m_xFemaleFI->set_sensitive(bEnablePersonal);
+ m_xFemaleColumnFT->set_sensitive(bEnablePersonal);
+ m_xFemaleColumnLB->set_sensitive(bEnablePersonal);
+ m_xFemaleFieldFT->set_sensitive(bEnablePersonal);
+ m_xFemaleFieldCB->set_sensitive(bEnablePersonal);
+ m_xNeutralFT->set_sensitive(bContainsGreeting);
+ m_xNeutralCB->set_sensitive(bContainsGreeting);
+}
+
+SwMailMergeGreetingsPage::SwMailMergeGreetingsPage(weld::Container* pPage, SwMailMergeWizard* pWizard)
+ : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmsalutationpage.ui", "MMSalutationPage")
+ , SwGreetingsHandler(pWizard->GetConfigItem(), *m_xBuilder)
+ , m_xPreview(new SwAddressPreview(m_xBuilder->weld_scrolled_window("previewwin", true)))
+ , m_xPreviewFI(m_xBuilder->weld_label("previewft"))
+ , m_xAssignPB(m_xBuilder->weld_button("assign"))
+ , m_xDocumentIndexFI(m_xBuilder->weld_label("documentindex"))
+ , m_xPrevSetIB(m_xBuilder->weld_button("prev"))
+ , m_xNextSetIB(m_xBuilder->weld_button("next"))
+ , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreview))
+{
+ m_pWizard = pWizard;
+
+ Size aSize(m_xPreview->GetDrawingArea()->get_ref_device().LogicToPixel(Size(186, 21), MapMode(MapUnit::MapAppFont)));
+ m_xPreviewWIN->set_size_request(aSize.Width(), aSize.Height());
+ m_sDocument = m_xDocumentIndexFI->get_label();
+
+ m_bIsTabPage = true;
+
+ m_xGreetingLineCB->connect_toggled(LINK(this, SwMailMergeGreetingsPage, ContainsHdl_Impl));
+ Link<weld::Toggleable&,void> aIndividualLink = LINK(this, SwGreetingsHandler, IndividualHdl_Impl);
+ m_xPersonalizedCB->connect_toggled(aIndividualLink);
+ Link<weld::Button&,void> aGreetingLink = LINK(this, SwGreetingsHandler, GreetingHdl_Impl);
+ m_xFemalePB->connect_clicked(aGreetingLink);
+ m_xMalePB->connect_clicked(aGreetingLink);
+ m_xAssignPB->connect_clicked(LINK(this, SwMailMergeGreetingsPage, AssignHdl_Impl));
+ Link<weld::ComboBox&,void> aLBoxLink2 = LINK(this, SwMailMergeGreetingsPage, GreetingSelectListBoxHdl_Impl);
+ m_xFemaleLB->connect_changed(aLBoxLink2);
+ m_xMaleLB->connect_changed(aLBoxLink2);
+ m_xFemaleColumnLB->connect_changed(aLBoxLink2);
+ m_xFemaleFieldCB->connect_changed(LINK(this, SwMailMergeGreetingsPage, GreetingSelectComboBoxHdl_Impl));
+ m_xNeutralCB->connect_changed(LINK(this, SwMailMergeGreetingsPage, GreetingSelectComboBoxHdl_Impl));
+
+ Link<weld::Button&,void> aDataLink = LINK(this, SwMailMergeGreetingsPage, InsertDataHdl_Impl);
+ m_xPrevSetIB->connect_clicked(aDataLink);
+ m_xNextSetIB->connect_clicked(aDataLink);
+
+ m_xGreetingLineCB->set_active(m_rConfigItem.IsGreetingLine(false));
+ m_xPersonalizedCB->set_active(m_rConfigItem.IsIndividualGreeting(false));
+ ContainsHdl_Impl(*m_xGreetingLineCB);
+ aIndividualLink.Call(*m_xPersonalizedCB);
+
+ lcl_FillGreetingsBox(*m_xFemaleLB, m_rConfigItem, SwMailMergeConfigItem::FEMALE);
+ lcl_FillGreetingsBox(*m_xMaleLB, m_rConfigItem, SwMailMergeConfigItem::MALE);
+ lcl_FillGreetingsBox(*m_xNeutralCB, m_rConfigItem, SwMailMergeConfigItem::NEUTRAL);
+
+ m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", "1"));
+}
+
+SwMailMergeGreetingsPage::~SwMailMergeGreetingsPage()
+{
+ m_xPreviewWIN.reset();
+ m_xPreview.reset();
+}
+
+void SwMailMergeGreetingsPage::Activate()
+{
+ //try to find the gender setting
+ m_xFemaleColumnLB->clear();
+ Reference< sdbcx::XColumnsSupplier > xColsSupp = m_rConfigItem.GetColumnsSupplier();
+ if(xColsSupp.is())
+ {
+ Reference < container::XNameAccess> xColAccess = xColsSupp->getColumns();
+ const Sequence< OUString > aColumns = xColAccess->getElementNames();
+ for(const auto& rColumn : aColumns)
+ m_xFemaleColumnLB->append_text(rColumn);
+ }
+
+ m_xFemaleColumnLB->set_active_text(m_rConfigItem.GetAssignedColumn(MM_PART_GENDER));
+ m_xFemaleColumnLB->save_value();
+
+ m_xFemaleFieldCB->set_entry_text(m_rConfigItem.GetFemaleGenderValue());
+ m_xFemaleFieldCB->save_value();
+
+ UpdatePreview();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE));
+}
+
+bool SwMailMergeGreetingsPage::commitPage( ::vcl::WizardTypes::CommitPageReason )
+{
+ if (m_xFemaleColumnLB->get_value_changed_from_saved())
+ {
+ const SwDBData& rDBData = m_rConfigItem.GetCurrentDBData();
+ Sequence< OUString> aAssignment = m_rConfigItem.GetColumnAssignment( rDBData );
+ if(aAssignment.getLength() <= MM_PART_GENDER)
+ aAssignment.realloc(MM_PART_GENDER + 1);
+ aAssignment.getArray()[MM_PART_GENDER] = m_xFemaleColumnLB->get_active_text();
+ m_rConfigItem.SetColumnAssignment( rDBData, aAssignment );
+ }
+ if (m_xFemaleFieldCB->get_value_changed_from_saved())
+ m_rConfigItem.SetFemaleGenderValue(m_xFemaleFieldCB->get_active_text());
+
+ lcl_StoreGreetingsBox(*m_xFemaleLB, m_rConfigItem, SwMailMergeConfigItem::FEMALE);
+ lcl_StoreGreetingsBox(*m_xMaleLB, m_rConfigItem, SwMailMergeConfigItem::MALE);
+
+ sal_Int32 nCurrentTextPos = m_xNeutralCB->find_text(m_xNeutralCB->get_active_text());
+ if (nCurrentTextPos == -1)
+ {
+ m_xNeutralCB->append_text(m_xNeutralCB->get_active_text());
+ m_xNeutralCB->set_active(m_xNeutralCB->get_count() - 1);
+ }
+ lcl_StoreGreetingsBox(*m_xNeutralCB, m_rConfigItem, SwMailMergeConfigItem::NEUTRAL);
+ m_rConfigItem.SetGreetingLine(m_xGreetingLineCB->get_active(), false);
+ m_rConfigItem.SetIndividualGreeting(m_xPersonalizedCB->get_active(), false);
+ return true;
+}
+
+IMPL_LINK(SwMailMergeGreetingsPage, ContainsHdl_Impl, weld::Toggleable&, rBox, void)
+{
+ bool bContainsGreeting = rBox.get_active();
+ SwGreetingsHandler::Contains(bContainsGreeting);
+ m_xPreviewFI->set_sensitive(bContainsGreeting);
+ m_xPreviewWIN->set_sensitive(bContainsGreeting);
+ m_xAssignPB->set_sensitive(bContainsGreeting);
+ m_xDocumentIndexFI->set_sensitive(bContainsGreeting);
+ m_xPrevSetIB->set_sensitive(bContainsGreeting);
+ m_xNextSetIB->set_sensitive(bContainsGreeting);
+ m_rConfigItem.SetGreetingLine(m_xGreetingLineCB->get_active(), false);
+ m_pWizard->UpdateRoadmap();
+ m_pWizard->enableButtons(WizardButtonFlags::NEXT, m_pWizard->isStateEnabled(MM_LAYOUTPAGE));
+}
+
+IMPL_LINK(SwMailMergeGreetingsPage, InsertDataHdl_Impl, weld::Button&, rButton, void)
+{
+ bool bNext = &rButton == m_xNextSetIB.get();
+ sal_Int32 nPos = m_rConfigItem.GetResultSetPosition();
+ m_rConfigItem.MoveResultSet( bNext ? ++nPos : --nPos);
+ nPos = m_rConfigItem.GetResultSetPosition();
+ bool bEnable = true;
+ if(nPos < 1)
+ {
+ bEnable = false;
+ nPos = 1;
+ }
+ else
+ UpdatePreview();
+ m_xPrevSetIB->set_sensitive(bEnable);
+ m_xNextSetIB->set_sensitive(bEnable);
+ m_xDocumentIndexFI->set_sensitive(bEnable);
+ m_xDocumentIndexFI->set_label(m_sDocument.replaceFirst("%1", OUString::number(nPos)));
+}
+
+SwMailBodyDialog::SwMailBodyDialog(weld::Window* pParent)
+ : SfxDialogController(pParent, "modules/swriter/ui/mmmailbody.ui", "MailBodyDialog")
+ , SwGreetingsHandler(*GetActiveView()->GetMailMergeConfigItem(), *m_xBuilder)
+ , m_xBodyFT(m_xBuilder->weld_label("bodyft"))
+ , m_xBodyMLE(m_xBuilder->weld_text_view("bodymle"))
+ , m_xOK(m_xBuilder->weld_button("ok"))
+{
+ m_bIsTabPage = false;
+ m_xBodyMLE->set_size_request(m_xBodyMLE->get_approximate_digit_width() * 45,
+ m_xBodyMLE->get_height_rows(6));
+ m_xGreetingLineCB->connect_toggled(LINK(this, SwMailBodyDialog, ContainsHdl_Impl));
+ Link<weld::Toggleable&,void> aIndividualLink = LINK(this, SwGreetingsHandler, IndividualHdl_Impl);
+ m_xPersonalizedCB->connect_toggled(aIndividualLink);
+ Link<weld::Button&,void> aGreetingLink = LINK(this, SwGreetingsHandler, GreetingHdl_Impl);
+ m_xFemalePB->connect_clicked(aGreetingLink);
+ m_xMalePB->connect_clicked(aGreetingLink);
+ m_xOK->connect_clicked(LINK(this, SwMailBodyDialog, OKHdl));
+
+ m_xGreetingLineCB->set_active(m_rConfigItem.IsGreetingLine(true));
+ m_xPersonalizedCB->set_active(m_rConfigItem.IsIndividualGreeting(true));
+ ContainsHdl_Impl(*m_xGreetingLineCB);
+ aIndividualLink.Call(*m_xPersonalizedCB);
+
+ lcl_FillGreetingsBox(*m_xFemaleLB, m_rConfigItem, SwMailMergeConfigItem::FEMALE);
+ lcl_FillGreetingsBox(*m_xMaleLB, m_rConfigItem, SwMailMergeConfigItem::MALE);
+ lcl_FillGreetingsBox(*m_xNeutralCB, m_rConfigItem, SwMailMergeConfigItem::NEUTRAL);
+
+ //try to find the gender setting
+ m_xFemaleColumnLB->clear();
+ Reference< sdbcx::XColumnsSupplier > xColsSupp = m_rConfigItem.GetColumnsSupplier();
+ if(xColsSupp.is())
+ {
+ Reference < container::XNameAccess> xColAccess = xColsSupp->getColumns();
+ const Sequence< OUString > aColumns = xColAccess->getElementNames();
+ for(const auto& rColumn : aColumns)
+ m_xFemaleColumnLB->append_text(rColumn);
+ }
+
+ m_xFemaleColumnLB->set_active_text(m_rConfigItem.GetAssignedColumn(MM_PART_GENDER));
+ m_xFemaleColumnLB->save_value();
+
+ m_xFemaleFieldCB->set_entry_text(m_rConfigItem.GetFemaleGenderValue());
+ m_xFemaleFieldCB->save_value();
+}
+
+SwMailBodyDialog::~SwMailBodyDialog()
+{
+}
+
+IMPL_LINK(SwMailBodyDialog, ContainsHdl_Impl, weld::Toggleable&, rBox, void)
+{
+ SwGreetingsHandler::Contains(rBox.get_active());
+ m_rConfigItem.SetGreetingLine(rBox.get_active(), true);
+}
+
+IMPL_LINK_NOARG(SwMailBodyDialog, OKHdl, weld::Button&, void)
+{
+ m_rConfigItem.SetGreetingLine(
+ m_xGreetingLineCB->get_active(), false);
+ m_rConfigItem.SetIndividualGreeting(
+ m_xPersonalizedCB->get_active(), false);
+
+ if (m_xFemaleColumnLB->get_value_changed_from_saved())
+ {
+ const SwDBData& rDBData = m_rConfigItem.GetCurrentDBData();
+ Sequence< OUString> aAssignment = m_rConfigItem.GetColumnAssignment( rDBData );
+ sal_Int32 nPos = m_xFemaleColumnLB->get_active();
+ if(aAssignment.getLength() < MM_PART_GENDER)
+ aAssignment.realloc(MM_PART_GENDER);
+ if( nPos > 0 )
+ aAssignment.getArray()[MM_PART_GENDER] = m_xFemaleColumnLB->get_active_text();
+ else
+ aAssignment.getArray()[MM_PART_GENDER].clear();
+ m_rConfigItem.SetColumnAssignment( rDBData, aAssignment );
+ }
+ if (m_xFemaleFieldCB->get_value_changed_from_saved())
+ m_rConfigItem.SetFemaleGenderValue(m_xFemaleFieldCB->get_active_text());
+
+ m_xDialog->response(RET_OK);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmgreetingspage.hxx b/sw/source/ui/dbui/mmgreetingspage.hxx
new file mode 100644
index 000000000..86aaf9c55
--- /dev/null
+++ b/sw/source/ui/dbui/mmgreetingspage.hxx
@@ -0,0 +1,131 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_MMGREETINGSPAGE_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_MMGREETINGSPAGE_HXX
+
+#include <vcl/wizardmachine.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <mailmergehelper.hxx>
+#include <vcl/weld.hxx>
+
+#include <mailmergewizard.hxx>
+
+class SwMailMergeWizard;
+
+class SwGreetingsHandler
+{
+protected:
+ SwMailMergeWizard* m_pWizard;
+ /// The mail merge state, available even when m_pWizard is nullptr.
+ SwMailMergeConfigItem& m_rConfigItem;
+ bool m_bIsTabPage;
+
+ std::unique_ptr<weld::CheckButton> m_xGreetingLineCB;
+ std::unique_ptr<weld::CheckButton> m_xPersonalizedCB;
+ std::unique_ptr<weld::Label> m_xFemaleFT;
+ std::unique_ptr<weld::ComboBox> m_xFemaleLB;
+ std::unique_ptr<weld::Button> m_xFemalePB;
+ std::unique_ptr<weld::Label> m_xMaleFT;
+ std::unique_ptr<weld::ComboBox> m_xMaleLB;
+ std::unique_ptr<weld::Button> m_xMalePB;
+ std::unique_ptr<weld::Label> m_xFemaleFI;
+ std::unique_ptr<weld::Label> m_xFemaleColumnFT;
+ std::unique_ptr<weld::ComboBox> m_xFemaleColumnLB;
+ std::unique_ptr<weld::Label> m_xFemaleFieldFT;
+ std::unique_ptr<weld::ComboBox> m_xFemaleFieldCB;
+ std::unique_ptr<weld::Label> m_xNeutralFT;
+ std::unique_ptr<weld::ComboBox> m_xNeutralCB;
+
+ SwGreetingsHandler(SwMailMergeConfigItem& rConfigItem, weld::Builder& rBuilder)
+ : m_pWizard(nullptr)
+ , m_rConfigItem(rConfigItem)
+ , m_bIsTabPage(false)
+ , m_xGreetingLineCB(rBuilder.weld_check_button("greeting"))
+ , m_xPersonalizedCB(rBuilder.weld_check_button("personalized"))
+ , m_xFemaleFT(rBuilder.weld_label("femaleft"))
+ , m_xFemaleLB(rBuilder.weld_combo_box("female"))
+ , m_xFemalePB(rBuilder.weld_button("newfemale"))
+ , m_xMaleFT(rBuilder.weld_label("maleft"))
+ , m_xMaleLB(rBuilder.weld_combo_box("male"))
+ , m_xMalePB(rBuilder.weld_button("newmale"))
+ , m_xFemaleFI(rBuilder.weld_label("femalefi"))
+ , m_xFemaleColumnFT(rBuilder.weld_label("femalecolft"))
+ , m_xFemaleColumnLB(rBuilder.weld_combo_box("femalecol"))
+ , m_xFemaleFieldFT(rBuilder.weld_label("femalefieldft"))
+ , m_xFemaleFieldCB(rBuilder.weld_combo_box("femalefield"))
+ , m_xNeutralFT(rBuilder.weld_label("generalft"))
+ , m_xNeutralCB(rBuilder.weld_combo_box("general"))
+ {
+ }
+
+ ~SwGreetingsHandler() {}
+
+ DECL_LINK(IndividualHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(GreetingHdl_Impl, weld::Button&, void);
+
+ void Contains(bool bContainsGreeting);
+ virtual void UpdatePreview();
+};
+
+class SwMailMergeGreetingsPage : public vcl::OWizardPage, public SwGreetingsHandler
+{
+ std::unique_ptr<SwAddressPreview> m_xPreview;
+ std::unique_ptr<weld::Label> m_xPreviewFI;
+ std::unique_ptr<weld::Button> m_xAssignPB;
+ std::unique_ptr<weld::Label> m_xDocumentIndexFI;
+ std::unique_ptr<weld::Button> m_xPrevSetIB;
+ std::unique_ptr<weld::Button> m_xNextSetIB;
+ std::unique_ptr<weld::CustomWeld> m_xPreviewWIN;
+
+ OUString m_sDocument;
+
+ DECL_LINK(ContainsHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(InsertDataHdl_Impl, weld::Button&, void);
+ DECL_LINK(GreetingSelectComboBoxHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(GreetingSelectListBoxHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(AssignHdl_Impl, weld::Button&, void);
+
+ virtual void UpdatePreview() override;
+ virtual void Activate() override;
+ virtual bool commitPage(::vcl::WizardTypes::CommitPageReason _eReason) override;
+
+public:
+ SwMailMergeGreetingsPage(weld::Container* pPage, SwMailMergeWizard* pWizard);
+ virtual ~SwMailMergeGreetingsPage() override;
+};
+
+class SwMailBodyDialog : public SfxDialogController, public SwGreetingsHandler
+{
+ std::unique_ptr<weld::Label> m_xBodyFT;
+ std::unique_ptr<weld::TextView> m_xBodyMLE;
+ std::unique_ptr<weld::Button> m_xOK;
+
+ DECL_LINK(ContainsHdl_Impl, weld::Toggleable&, void);
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+public:
+ SwMailBodyDialog(weld::Window* pParent);
+ virtual ~SwMailBodyDialog() override;
+
+ void SetBody(const OUString& rBody) { m_xBodyMLE->set_text(rBody); }
+ OUString GetBody() const { return m_xBodyMLE->get_text(); }
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmlayoutpage.cxx b/sw/source/ui/dbui/mmlayoutpage.cxx
new file mode 100644
index 000000000..86d705b2b
--- /dev/null
+++ b/sw/source/ui/dbui/mmlayoutpage.cxx
@@ -0,0 +1,692 @@
+/* -*- 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 <swtypes.hxx>
+#include "mmlayoutpage.hxx"
+#include <mailmergewizard.hxx>
+#include <mmconfigitem.hxx>
+#include <mailmergehelper.hxx>
+#include <unotools.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <i18nutil/unicode.hxx>
+#include <unotools/tempfile.hxx>
+#include <uitool.hxx>
+#include <view.hxx>
+#include <swundo.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svtools/unitconv.hxx>
+#include <com/sun/star/view/XViewSettingsSupplier.hpp>
+#include <com/sun/star/view/DocumentZoomType.hpp>
+#include <fldmgr.hxx>
+#include <fldbas.hxx>
+#include <unotxdoc.hxx>
+#include <docsh.hxx>
+#include <doc.hxx>
+#include <wrtsh.hxx>
+#include <fmtsrnd.hxx>
+#include <pagedesc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <editeng/boxitem.hxx>
+#include <osl/file.hxx>
+#include <vcl/settings.hxx>
+#include <unoprnms.hxx>
+
+#include <dbui.hrc>
+
+using namespace osl;
+using namespace svt;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::view;
+
+constexpr tools::Long DEFAULT_LEFT_DISTANCE = o3tl::toTwips(25, o3tl::Length::mm); // 2,5 cm
+constexpr tools::Long DEFAULT_TOP_DISTANCE = o3tl::toTwips(55, o3tl::Length::mm); // 5,5 cm
+constexpr tools::Long GREETING_TOP_DISTANCE = o3tl::toTwips(125, o3tl::Length::mm); //12,5 cm
+constexpr tools::Long DEFAULT_ADDRESS_WIDTH = o3tl::toTwips(75, o3tl::Length::mm); // 7,5 cm
+constexpr tools::Long DEFAULT_ADDRESS_HEIGHT = o3tl::toTwips(35, o3tl::Length::mm); // 3,5cm
+
+SwMailMergeLayoutPage::SwMailMergeLayoutPage(weld::Container* pPage, SwMailMergeWizard* pWizard)
+ : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmlayoutpage.ui", "MMLayoutPage")
+ , m_pExampleWrtShell(nullptr)
+ , m_pAddressBlockFormat(nullptr)
+ , m_bIsGreetingInserted(false)
+ , m_pWizard(pWizard)
+ , m_xPosition(m_xBuilder->weld_container("addresspos"))
+ , m_xAlignToBodyCB(m_xBuilder->weld_check_button("align"))
+ , m_xLeftFT(m_xBuilder->weld_label("leftft"))
+ , m_xLeftMF(m_xBuilder->weld_metric_spin_button("left", FieldUnit::CM))
+ , m_xTopMF(m_xBuilder->weld_metric_spin_button("top", FieldUnit::CM))
+ , m_xGreetingLine(m_xBuilder->weld_container("greetingspos"))
+ , m_xUpPB(m_xBuilder->weld_button("up"))
+ , m_xDownPB(m_xBuilder->weld_button("down"))
+ , m_xZoomLB(m_xBuilder->weld_combo_box("zoom"))
+{
+ std::shared_ptr<const SfxFilter> pSfxFlt =
+ SwDocShell::Factory().GetFilterContainer()->
+ GetFilter4FilterName("writer8", SfxFilterFlags::EXPORT);
+
+ //save the current document into a temporary file
+ {
+ //temp file needs its own block
+ //creating with extension is not supported by a static method :-(
+ OUString const sExt(
+ comphelper::string::stripStart(pSfxFlt->GetDefaultExtension(),'*'));
+ utl::TempFile aTempFile( u"", true, &sExt );
+ m_sExampleURL = aTempFile.GetURL();
+ aTempFile.EnableKillingFile();
+ }
+ SwView* pView = m_pWizard->GetSwView();
+ // Don't save embedded data set! It would steal it from current document.
+ uno::Sequence< beans::PropertyValue > aValues =
+ {
+ comphelper::makePropertyValue("FilterName", pSfxFlt->GetFilterName()),
+ comphelper::makePropertyValue("NoEmbDataSet", true)
+ };
+
+ uno::Reference< frame::XStorable > xStore( pView->GetDocShell()->GetModel(), uno::UNO_QUERY);
+ xStore->storeToURL( m_sExampleURL, aValues );
+
+ Link<SwOneExampleFrame&,void> aLink(LINK(this, SwMailMergeLayoutPage, PreviewLoadedHdl_Impl));
+ m_xExampleFrame.reset(new SwOneExampleFrame(EX_SHOW_DEFAULT_PAGE, &aLink, &m_sExampleURL));
+ m_xExampleContainerWIN.reset(new weld::CustomWeld(*m_xBuilder, "example", *m_xExampleFrame));
+
+ Size aSize = m_xExampleFrame->GetDrawingArea()->get_ref_device().LogicToPixel(
+ Size(124, 159), MapMode(MapUnit::MapAppFont));
+ m_xExampleFrame->set_size_request(aSize.Width(), aSize.Height());
+
+ m_xExampleContainerWIN->hide();
+
+ m_xLeftMF->set_value(m_xLeftMF->normalize(DEFAULT_LEFT_DISTANCE), FieldUnit::TWIP);
+ m_xTopMF->set_value(m_xTopMF->normalize(DEFAULT_TOP_DISTANCE), FieldUnit::TWIP);
+
+ const LanguageTag& rLang = Application::GetSettings().GetUILanguageTag();
+ m_xZoomLB->append_text(unicode::formatPercent(50, rLang));
+ m_xZoomLB->append_text(unicode::formatPercent(75, rLang));
+ m_xZoomLB->append_text(unicode::formatPercent(100, rLang));
+ m_xZoomLB->set_active(0); //page size
+ m_xZoomLB->connect_changed(LINK(this, SwMailMergeLayoutPage, ZoomHdl_Impl));
+
+ Link<weld::MetricSpinButton&,void> aFrameHdl = LINK(this, SwMailMergeLayoutPage, ChangeAddressHdl_Impl);
+ m_xLeftMF->connect_value_changed(aFrameHdl);
+ m_xTopMF->connect_value_changed(aFrameHdl);
+
+ FieldUnit eFieldUnit = ::GetDfltMetric(false);
+ ::SetFieldUnit( *m_xLeftMF, eFieldUnit );
+ ::SetFieldUnit( *m_xTopMF, eFieldUnit );
+
+ Link<weld::Button&,void> aUpDownHdl = LINK(this, SwMailMergeLayoutPage, GreetingsHdl_Impl );
+ m_xUpPB->connect_clicked(aUpDownHdl);
+ m_xDownPB->connect_clicked(aUpDownHdl);
+ m_xAlignToBodyCB->connect_toggled(LINK(this, SwMailMergeLayoutPage, AlignToTextHdl_Impl));
+ m_xAlignToBodyCB->set_active(true);
+}
+
+SwMailMergeLayoutPage::~SwMailMergeLayoutPage()
+{
+ File::remove( m_sExampleURL );
+}
+
+void SwMailMergeLayoutPage::Activate()
+{
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ bool bGreetingLine = rConfigItem.IsGreetingLine(false) && !rConfigItem.IsGreetingInserted();
+ bool bAddressBlock = rConfigItem.IsAddressBlock() && !rConfigItem.IsAddressInserted();
+
+ m_xPosition->set_sensitive(bAddressBlock);
+ AlignToTextHdl_Impl(*m_xAlignToBodyCB);
+
+ m_xGreetingLine->set_sensitive(bGreetingLine);
+
+ //check if greeting and/or address frame have to be inserted/removed
+ if(!m_pExampleWrtShell) // initially there's nothing to check
+ return;
+
+ if(!rConfigItem.IsGreetingInserted() &&
+ m_bIsGreetingInserted != bGreetingLine )
+ {
+ if( m_bIsGreetingInserted )
+ {
+ m_pExampleWrtShell->DelFullPara();
+ m_bIsGreetingInserted = false;
+ }
+ else
+ {
+ InsertGreeting(*m_pExampleWrtShell, m_pWizard->GetConfigItem(), true);
+ m_bIsGreetingInserted = true;
+ }
+ }
+ if(!rConfigItem.IsAddressInserted() &&
+ rConfigItem.IsAddressBlock() != ( nullptr != m_pAddressBlockFormat ))
+ {
+ if( m_pAddressBlockFormat )
+ {
+ m_pExampleWrtShell->Push();
+ m_pExampleWrtShell->GotoFly( m_pAddressBlockFormat->GetName() );
+ m_pExampleWrtShell->DelRight();
+ m_pAddressBlockFormat = nullptr;
+ m_pExampleWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ else
+ {
+ tools::Long nLeft = static_cast< tools::Long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP)));
+ tools::Long nTop = static_cast< tools::Long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP)));
+ m_pAddressBlockFormat = InsertAddressFrame(
+ *m_pExampleWrtShell, m_pWizard->GetConfigItem(),
+ Point(nLeft, nTop),
+ m_xAlignToBodyCB->get_active(), true);
+ }
+ }
+ m_xExampleFrame->Invalidate();
+}
+
+bool SwMailMergeLayoutPage::commitPage(::vcl::WizardTypes::CommitPageReason eReason)
+{
+ //now insert the frame and the greeting
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ if (eReason == ::vcl::WizardTypes::eTravelForward || eReason == ::vcl::WizardTypes::eFinish)
+ {
+ tools::Long nLeft = static_cast< tools::Long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP)));
+ tools::Long nTop = static_cast< tools::Long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP)));
+ InsertAddressAndGreeting(
+ m_pWizard->GetSwView(),
+ rConfigItem,
+ Point(nLeft, nTop),
+ m_xAlignToBodyCB->get_active());
+ }
+ return true;
+}
+
+SwFrameFormat* SwMailMergeLayoutPage::InsertAddressAndGreeting(SwView const * pView,
+ SwMailMergeConfigItem& rConfigItem,
+ const Point& rAddressPosition,
+ bool bAlignToBody)
+{
+ SwFrameFormat* pAddressBlockFormat = nullptr;
+ pView->GetWrtShell().StartUndo(SwUndoId::INSERT);
+ if(rConfigItem.IsAddressBlock() && !rConfigItem.IsAddressInserted())
+ {
+ //insert the frame
+ Point aAddressPosition(DEFAULT_LEFT_DISTANCE, DEFAULT_TOP_DISTANCE);
+ if(rAddressPosition.X() > 0 && rAddressPosition.Y() > 0)
+ aAddressPosition = rAddressPosition;
+ pAddressBlockFormat = InsertAddressFrame( pView->GetWrtShell(),
+ rConfigItem,
+ aAddressPosition, bAlignToBody, false);
+ rConfigItem.SetAddressInserted();
+ }
+ //now the greeting
+ if(rConfigItem.IsGreetingLine(false) && !rConfigItem.IsGreetingInserted())
+ {
+ InsertGreeting( pView->GetWrtShell(), rConfigItem, false);
+ rConfigItem.SetGreetingInserted();
+ }
+ pView->GetWrtShell().EndUndo(SwUndoId::INSERT);
+ return pAddressBlockFormat;
+}
+
+SwFrameFormat* SwMailMergeLayoutPage::InsertAddressFrame(
+ SwWrtShell& rShell,
+ SwMailMergeConfigItem const & rConfigItem,
+ const Point& rDestination,
+ bool bAlignLeft,
+ bool bExample)
+{
+ // insert the address block and the greeting line
+ SfxItemSetFixed<
+ RES_FRM_SIZE, RES_FRM_SIZE,
+ RES_SURROUND, RES_ANCHOR,
+ RES_BOX, RES_BOX> aSet( rShell.GetAttrPool() );
+ aSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
+ if(bAlignLeft)
+ aSet.Put(SwFormatHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA ));
+ else
+ aSet.Put(SwFormatHoriOrient( rDestination.X(), text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
+ aSet.Put(SwFormatVertOrient( rDestination.Y(), text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
+ aSet.Put(SwFormatFrameSize( SwFrameSize::Minimum, DEFAULT_ADDRESS_WIDTH, DEFAULT_ADDRESS_HEIGHT ));
+ // the example gets a border around the frame, the real document doesn't get one
+ if(!bExample)
+ aSet.Put(SvxBoxItem( RES_BOX ));
+ aSet.Put(SwFormatSurround( css::text::WrapTextMode_NONE ));
+
+ rShell.NewFlyFrame(aSet, true );
+ SwFrameFormat* pRet = rShell.GetFlyFrameFormat();
+ OSL_ENSURE( pRet, "Fly not inserted" );
+
+ rShell.UnSelectFrame();
+ const Sequence< OUString> aBlocks = rConfigItem.GetAddressBlocks();
+ if(bExample)
+ {
+ rShell.Insert(aBlocks[0]);
+ }
+ else
+ {
+ //the placeholders should be replaced by the appropriate fields
+ SwFieldMgr aFieldMgr(&rShell);
+ //create a database string source.command.commandtype.column
+ const SwDBData& rData = rConfigItem.GetCurrentDBData();
+ OUString sDBName(rData.sDataSource + OUStringChar(DB_DELIM)
+ + rData.sCommand + OUStringChar(DB_DELIM));
+ const OUString sDatabaseConditionPrefix(sDBName.replace(DB_DELIM, '.'));
+ sDBName += OUString::number(rData.nCommandType) + OUStringChar(DB_DELIM);
+
+ // if only the country is in an address line the
+ // paragraph has to be hidden depending on the
+ // IsIncludeCountry()/GetExcludeCountry() settings
+
+ bool bIncludeCountry = rConfigItem.IsIncludeCountry();
+ bool bHideEmptyParagraphs = rConfigItem.IsHideEmptyParagraphs();
+ const OUString rExcludeCountry = rConfigItem.GetExcludeCountry();
+ bool bSpecialReplacementForCountry = (!bIncludeCountry || !rExcludeCountry.isEmpty());
+
+ const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders();
+ Sequence< OUString> aAssignment =
+ rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() );
+ const OUString* pAssignment = aAssignment.getConstArray();
+ const OUString sCountryColumn(
+ (aAssignment.getLength() > MM_PART_COUNTRY && !aAssignment[MM_PART_COUNTRY].isEmpty())
+ ? aAssignment[MM_PART_COUNTRY]
+ : rHeaders[MM_PART_COUNTRY].first);
+
+ OUString sHideParagraphsExpression;
+ SwAddressIterator aIter(aBlocks[0]);
+ while(aIter.HasMore())
+ {
+ SwMergeAddressItem aItem = aIter.Next();
+ if(aItem.bIsColumn)
+ {
+ OUString sConvertedColumn = aItem.sText;
+ auto nSize = std::min(static_cast<sal_uInt32>(rHeaders.size()),
+ static_cast<sal_uInt32>(aAssignment.getLength()));
+ for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn)
+ {
+ if (rHeaders[nColumn].first == aItem.sText &&
+ !pAssignment[nColumn].isEmpty())
+ {
+ sConvertedColumn = pAssignment[nColumn];
+ break;
+ }
+ }
+ const OUString sDB(sDBName + sConvertedColumn);
+
+ if(!sHideParagraphsExpression.isEmpty())
+ sHideParagraphsExpression += " AND ";
+ sHideParagraphsExpression += "![" + sDatabaseConditionPrefix + sConvertedColumn + "]";
+
+ if( bSpecialReplacementForCountry && sCountryColumn == sConvertedColumn )
+ {
+ // now insert a hidden paragraph field
+ if( !rExcludeCountry.isEmpty() )
+ {
+ const OUString sExpression("[" + sDatabaseConditionPrefix + sCountryColumn + "]");
+ SwInsertField_Data aData(SwFieldTypesEnum::ConditionalText, 0,
+ sExpression + " != \"" + rExcludeCountry + "\"",
+ sExpression,
+ 0, &rShell );
+ aFieldMgr.InsertField( aData );
+ }
+ else
+ {
+ SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, "", "", 0, &rShell );
+ aFieldMgr.InsertField( aData );
+ }
+ }
+ else
+ {
+ SwInsertField_Data aData(SwFieldTypesEnum::Database, 0, sDB, OUString(), 0, &rShell);
+ aFieldMgr.InsertField( aData );
+ }
+ }
+ else if(!aItem.bIsReturn)
+ {
+ rShell.Insert(aItem.sText);
+ }
+ else
+ {
+ if(bHideEmptyParagraphs)
+ {
+ SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sHideParagraphsExpression, OUString(), 0, &rShell);
+ aFieldMgr.InsertField( aData );
+ }
+ sHideParagraphsExpression.clear();
+ //now add a new paragraph
+ rShell.SplitNode();
+ }
+ }
+ if(bHideEmptyParagraphs && !sHideParagraphsExpression.isEmpty())
+ {
+ SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sHideParagraphsExpression, OUString(), 0, &rShell);
+ aFieldMgr.InsertField( aData );
+ }
+ }
+ return pRet;
+}
+
+void SwMailMergeLayoutPage::InsertGreeting(SwWrtShell& rShell, SwMailMergeConfigItem const & rConfigItem, bool bExample)
+{
+ //set the cursor to the desired position - if no text content is here then
+ //new paragraphs are inserted
+ const SwRect& rPageRect = rShell.GetAnyCurRect(CurRectType::Page);
+ const Point aGreetingPos( DEFAULT_LEFT_DISTANCE + rPageRect.Left(), GREETING_TOP_DISTANCE );
+
+ const bool bRet = rShell.SetShadowCursorPos( aGreetingPos, SwFillMode::TabSpace );
+
+ if(!bRet)
+ {
+ //there's already text at the desired position
+ //go to start of the doc, directly!
+ rShell.SttEndDoc(true);
+ //and go by paragraph until the position is reached
+ tools::Long nYPos = rShell.GetCharRect().Top();
+ while(nYPos < GREETING_TOP_DISTANCE)
+ {
+ if(!rShell.FwdPara())
+ break;
+ nYPos = rShell.GetCharRect().Top();
+ }
+ //text needs to be appended
+ while(nYPos < GREETING_TOP_DISTANCE)
+ {
+ if(!rShell.AppendTextNode())
+ break;
+ nYPos = rShell.GetCharRect().Top();
+ }
+ }
+ else
+ {
+ //we may end up inside of a paragraph if the left margin is not at DEFAULT_LEFT_DISTANCE
+ rShell.MovePara(GoCurrPara, fnParaStart);
+ }
+ bool bSplitNode = !rShell.IsEndPara();
+ SwNodeOffset nMoves(rConfigItem.GetGreetingMoves());
+ if( !bExample && SwNodeOffset(0) != nMoves )
+ {
+ if(nMoves < SwNodeOffset(0))
+ {
+ rShell.MoveParagraph( nMoves );
+ }
+ else
+ while(nMoves)
+ {
+ bool bMoved = rShell.MoveParagraph();
+ if(!bMoved)
+ {
+ //insert a new paragraph before the greeting line
+ rShell.SplitNode();
+ }
+ --nMoves;
+ }
+ }
+ //now insert the greeting text - if we have any?
+ const bool bIndividual = rConfigItem.IsIndividualGreeting(false);
+ if(bIndividual)
+ {
+ //lock expression fields - prevents hiding of the paragraph to insert into
+ rShell.LockExpFields();
+ if(bExample)
+ {
+ for(sal_Int8 eGender = SwMailMergeConfigItem::FEMALE;
+ eGender <= SwMailMergeConfigItem::NEUTRAL; ++eGender)
+ {
+ Sequence< OUString > aEntries =
+ rConfigItem.GetGreetings(static_cast<SwMailMergeConfigItem::Gender>(eGender));
+ sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(static_cast<SwMailMergeConfigItem::Gender>(eGender));
+ if( nCurrent >= 0 && nCurrent < aEntries.getLength())
+ {
+ // Greeting
+ rShell.Insert(aEntries[nCurrent]);
+ break;
+ }
+ }
+ }
+ else
+ {
+ SwFieldMgr aFieldMgr(&rShell);
+ //three paragraphs, each with an appropriate hidden paragraph field
+ //are to be inserted
+
+ //name of the gender column
+ const OUString sGenderColumn = rConfigItem.GetAssignedColumn(MM_PART_GENDER);
+ const OUString sNameColumn = rConfigItem.GetAssignedColumn(MM_PART_LASTNAME);
+
+ const OUString& rFemaleGenderValue = rConfigItem.GetFemaleGenderValue();
+ bool bHideEmptyParagraphs = rConfigItem.IsHideEmptyParagraphs();
+ const SwDBData& rData = rConfigItem.GetCurrentDBData();
+ const OUString sCommonBase(rData.sDataSource + "." + rData.sCommand + ".");
+ const OUString sConditionBase("[" + sCommonBase + sGenderColumn + "]");
+ const OUString sNameColumnBase("[" + sCommonBase + sNameColumn + "]");
+
+ const OUString sDBName(rData.sDataSource + OUStringChar(DB_DELIM)
+ + rData.sCommand + OUStringChar(DB_DELIM)
+ + OUString::number(rData.nCommandType) + OUStringChar(DB_DELIM));
+
+// Female: [database.sGenderColumn] != "rFemaleGenderValue" && [database.NameColumn]
+// Male: [database.sGenderColumn] == "rFemaleGenderValue" && [database.rGenderColumn]
+// Neutral: [database.sNameColumn]
+ OSL_ENSURE(!sGenderColumn.isEmpty() && !rFemaleGenderValue.isEmpty(),
+ "gender settings not available - how to form the condition?");
+ //column used as lastname
+ for(sal_Int8 eGender = SwMailMergeConfigItem::FEMALE;
+ eGender <= SwMailMergeConfigItem::NEUTRAL; ++eGender)
+ {
+ Sequence< OUString> aEntries = rConfigItem.GetGreetings(static_cast<SwMailMergeConfigItem::Gender>(eGender));
+ sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(static_cast<SwMailMergeConfigItem::Gender>(eGender));
+ if( nCurrent >= 0 && nCurrent < aEntries.getLength())
+ {
+ const OUString sGreeting = aEntries[nCurrent];
+ OUString sCondition;
+ OUString sHideParagraphsExpression;
+ switch(eGender)
+ {
+ case SwMailMergeConfigItem::FEMALE:
+ sCondition = sConditionBase + " != \"" + rFemaleGenderValue
+ + "\" OR NOT " + sNameColumnBase;
+ sHideParagraphsExpression = "!" + sNameColumnBase;
+ break;
+ case SwMailMergeConfigItem::MALE:
+ sCondition = sConditionBase + " == \"" + rFemaleGenderValue
+ + "\" OR NOT " + sNameColumnBase;
+ break;
+ case SwMailMergeConfigItem::NEUTRAL:
+ sCondition = sNameColumnBase;
+ break;
+ }
+
+ if(bHideEmptyParagraphs && !sHideParagraphsExpression.isEmpty())
+ {
+ OUString sComplete = "(" + sCondition + ") OR (" + sHideParagraphsExpression + ")";
+ SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sComplete, OUString(), 0, &rShell);
+ aFieldMgr.InsertField( aData );
+ }
+ else
+ {
+ SwInsertField_Data aData(SwFieldTypesEnum::HiddenParagraph, 0, sCondition, OUString(), 0, &rShell);
+ aFieldMgr.InsertField( aData );
+ }
+ //now the text has to be inserted
+ const std::vector<std::pair<OUString, int>>& rHeaders = rConfigItem.GetDefaultAddressHeaders();
+ Sequence< OUString> aAssignment =
+ rConfigItem.GetColumnAssignment( rConfigItem.GetCurrentDBData() );
+ const OUString* pAssignment = aAssignment.getConstArray();
+ SwAddressIterator aIter(sGreeting);
+ while(aIter.HasMore())
+ {
+ SwMergeAddressItem aItem = aIter.Next();
+ if(aItem.bIsColumn)
+ {
+ OUString sConvertedColumn = aItem.sText;
+ auto nSize = std::min(static_cast<sal_uInt32>(rHeaders.size()),
+ static_cast<sal_uInt32>(aAssignment.getLength()));
+ for(sal_uInt32 nColumn = 0; nColumn < nSize; ++nColumn)
+ {
+ if (rHeaders[nColumn].first == aItem.sText &&
+ !pAssignment[nColumn].isEmpty())
+ {
+ sConvertedColumn = pAssignment[nColumn];
+ break;
+ }
+ }
+ SwInsertField_Data aData(SwFieldTypesEnum::Database, 0,
+ sDBName + sConvertedColumn,
+ OUString(), 0, &rShell);
+ aFieldMgr.InsertField( aData );
+ }
+ else
+ {
+ rShell.Insert(aItem.sText);
+ }
+ }
+ //now add a new paragraph
+ rShell.SplitNode();
+ }
+ }
+
+ }
+ rShell.UnlockExpFields();
+ }
+ else
+ {
+ Sequence< OUString> aEntries = rConfigItem.GetGreetings(SwMailMergeConfigItem::NEUTRAL);
+ sal_Int32 nCurrent = rConfigItem.GetCurrentGreeting(SwMailMergeConfigItem::NEUTRAL);
+ // Greeting
+ rShell.Insert(( nCurrent >= 0 && nCurrent < aEntries.getLength() )
+ ? aEntries[nCurrent] : OUString());
+ }
+ // now insert a new paragraph here if necessary
+ if(bSplitNode)
+ {
+ rShell.Push();
+ rShell.SplitNode();
+ rShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ //put the cursor to the start of the paragraph
+ rShell.SttPara();
+
+ OSL_ENSURE(nullptr == rShell.GetTableFormat(), "What to do with a table here?");
+}
+
+IMPL_LINK_NOARG(SwMailMergeLayoutPage, PreviewLoadedHdl_Impl, SwOneExampleFrame&, void)
+{
+ m_xExampleContainerWIN->show();
+
+ Reference< XModel > & xModel = m_xExampleFrame->GetModel();
+ //now the ViewOptions should be set properly
+ Reference< XViewSettingsSupplier > xSettings(xModel->getCurrentController(), UNO_QUERY);
+ m_xViewProperties = xSettings->getViewSettings();
+ auto pXDoc = comphelper::getFromUnoTunnel<SwXTextDocument>(xModel);
+ SwDocShell* pDocShell = pXDoc->GetDocShell();
+ m_pExampleWrtShell = pDocShell->GetWrtShell();
+ OSL_ENSURE(m_pExampleWrtShell, "No SwWrtShell found!");
+ if(!m_pExampleWrtShell)
+ return;
+
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ if(rConfigItem.IsAddressBlock())
+ {
+ m_pAddressBlockFormat = InsertAddressFrame(
+ *m_pExampleWrtShell, rConfigItem,
+ Point(DEFAULT_LEFT_DISTANCE, DEFAULT_TOP_DISTANCE),
+ m_xAlignToBodyCB->get_active(), true);
+ }
+ if(rConfigItem.IsGreetingLine(false))
+ {
+ InsertGreeting(*m_pExampleWrtShell, rConfigItem, true);
+ m_bIsGreetingInserted = true;
+ }
+
+ ZoomHdl_Impl(*m_xZoomLB);
+
+ const SwFormatFrameSize& rPageSize = m_pExampleWrtShell->GetPageDesc(
+ m_pExampleWrtShell->GetCurPageDesc()).GetMaster().GetFrameSize();
+ m_xLeftMF->set_max(rPageSize.GetWidth() - DEFAULT_LEFT_DISTANCE, FieldUnit::NONE);
+ m_xTopMF->set_max(rPageSize.GetHeight() - DEFAULT_TOP_DISTANCE, FieldUnit::NONE);
+}
+
+IMPL_LINK(SwMailMergeLayoutPage, ZoomHdl_Impl, weld::ComboBox&, rBox, void)
+{
+ if (!m_pExampleWrtShell)
+ return;
+
+ sal_Int16 eType = DocumentZoomType::BY_VALUE;
+ short nZoom = 50;
+ switch (rBox.get_active())
+ {
+ case 0 : eType = DocumentZoomType::ENTIRE_PAGE; break;
+ case 1 : nZoom = 50; break;
+ case 2 : nZoom = 75; break;
+ case 3 : nZoom = 100; break;
+ }
+ Any aZoom;
+ aZoom <<= eType;
+ m_xViewProperties->setPropertyValue(UNO_NAME_ZOOM_TYPE, aZoom);
+ aZoom <<= nZoom;
+ m_xViewProperties->setPropertyValue(UNO_NAME_ZOOM_VALUE, aZoom);
+
+ m_xExampleFrame->Invalidate();
+}
+
+IMPL_LINK_NOARG(SwMailMergeLayoutPage, ChangeAddressHdl_Impl, weld::MetricSpinButton&, void)
+{
+ if(!(m_pExampleWrtShell && m_pAddressBlockFormat))
+ return;
+
+ tools::Long nLeft = static_cast< tools::Long >(m_xLeftMF->denormalize(m_xLeftMF->get_value(FieldUnit::TWIP)));
+ tools::Long nTop = static_cast< tools::Long >(m_xTopMF->denormalize(m_xTopMF->get_value(FieldUnit::TWIP)));
+
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_ANCHOR> aSet(
+ m_pExampleWrtShell->GetAttrPool());
+ if (m_xAlignToBodyCB->get_active())
+ aSet.Put(SwFormatHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::PAGE_PRINT_AREA ));
+ else
+ aSet.Put(SwFormatHoriOrient( nLeft, text::HoriOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
+ aSet.Put(SwFormatVertOrient( nTop, text::VertOrientation::NONE, text::RelOrientation::PAGE_FRAME ));
+ m_pExampleWrtShell->GetDoc()->SetFlyFrameAttr( *m_pAddressBlockFormat, aSet );
+ m_xExampleFrame->Invalidate();
+}
+
+IMPL_LINK(SwMailMergeLayoutPage, GreetingsHdl_Impl, weld::Button&, rButton, void)
+{
+ bool bDown = &rButton == m_xDownPB.get();
+ bool bMoved = m_pExampleWrtShell->MoveParagraph( SwNodeOffset(bDown ? 1 : -1) );
+ if (bMoved || bDown)
+ m_pWizard->GetConfigItem().MoveGreeting(bDown ? 1 : -1 );
+ if(!bMoved && bDown)
+ {
+ //insert a new paragraph before the greeting line
+ m_pExampleWrtShell->SplitNode();
+ }
+ m_xExampleFrame->Invalidate();
+}
+
+IMPL_LINK(SwMailMergeLayoutPage, AlignToTextHdl_Impl, weld::Toggleable&, rBox, void)
+{
+ bool bCheck = rBox.get_active() && rBox.get_sensitive();
+ m_xLeftFT->set_sensitive(!bCheck);
+ m_xLeftMF->set_sensitive(!bCheck);
+ ChangeAddressHdl_Impl( *m_xLeftMF );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmlayoutpage.hxx b/sw/source/ui/dbui/mmlayoutpage.hxx
new file mode 100644
index 000000000..abec12bb2
--- /dev/null
+++ b/sw/source/ui/dbui/mmlayoutpage.hxx
@@ -0,0 +1,87 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_MMLAYOUTPAGE_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_MMLAYOUTPAGE_HXX
+
+#include <vcl/wizardmachine.hxx>
+#include <mailmergehelper.hxx>
+#include <com/sun/star/uno/Reference.h>
+
+class SwMailMergeWizard;
+class SwFrameFormat;
+class SwOneExampleFrame;
+class SwWrtShell;
+class SwView;
+
+namespace com::sun::star::beans{ class XPropertySet;}
+
+class SwMailMergeLayoutPage : public vcl::OWizardPage
+{
+ SwWrtShell* m_pExampleWrtShell;
+
+ OUString m_sExampleURL;
+ SwFrameFormat* m_pAddressBlockFormat;
+
+ bool m_bIsGreetingInserted;
+
+ SwMailMergeWizard* m_pWizard;
+
+ css::uno::Reference< css::beans::XPropertySet > m_xViewProperties;
+
+ std::unique_ptr<weld::Container> m_xPosition;
+ std::unique_ptr<weld::CheckButton> m_xAlignToBodyCB;
+ std::unique_ptr<weld::Label> m_xLeftFT;
+ std::unique_ptr<weld::MetricSpinButton> m_xLeftMF;
+ std::unique_ptr<weld::MetricSpinButton> m_xTopMF;
+ std::unique_ptr<weld::Container> m_xGreetingLine;
+ std::unique_ptr<weld::Button> m_xUpPB;
+ std::unique_ptr<weld::Button> m_xDownPB;
+ std::unique_ptr<weld::ComboBox> m_xZoomLB;
+ std::unique_ptr<SwOneExampleFrame> m_xExampleFrame;
+ std::unique_ptr<weld::CustomWeld> m_xExampleContainerWIN;
+
+ DECL_LINK(PreviewLoadedHdl_Impl, SwOneExampleFrame&, void);
+ DECL_LINK(ZoomHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(ChangeAddressHdl_Impl, weld::MetricSpinButton&, void);
+ DECL_LINK(GreetingsHdl_Impl, weld::Button&, void);
+ DECL_LINK(AlignToTextHdl_Impl, weld::Toggleable&, void);
+
+ static SwFrameFormat* InsertAddressFrame(
+ SwWrtShell& rShell,
+ SwMailMergeConfigItem const & rConfigItem,
+ const Point& rDestination,
+ bool bAlignToBody,
+ bool bExample);
+ static void InsertGreeting(SwWrtShell& rShell, SwMailMergeConfigItem const & rConfigItem, bool bExample);
+
+ virtual void Activate() override;
+ virtual bool commitPage(::vcl::WizardTypes::CommitPageReason _eReason) override;
+public:
+ SwMailMergeLayoutPage(weld::Container* pPage, SwMailMergeWizard* pWizard);
+ virtual ~SwMailMergeLayoutPage() override;
+
+ static SwFrameFormat* InsertAddressAndGreeting(SwView const * pView,
+ SwMailMergeConfigItem& rConfigItem,
+ const Point& rAddressPos,
+ bool bAlignToBody);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmoutputtypepage.cxx b/sw/source/ui/dbui/mmoutputtypepage.cxx
new file mode 100644
index 000000000..ad0ed9c12
--- /dev/null
+++ b/sw/source/ui/dbui/mmoutputtypepage.cxx
@@ -0,0 +1,532 @@
+/* -*- 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 "mmoutputtypepage.hxx"
+#include <mailmergewizard.hxx>
+#include <mmconfigitem.hxx>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <swtypes.hxx>
+
+#include <rtl/ref.hxx>
+#include <com/sun/star/mail/XSmtpService.hpp>
+#include <vcl/idle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <swunohelper.hxx>
+#include <mmresultdialogs.hxx>
+#include <maildispatcher.hxx>
+#include <imaildsplistener.hxx>
+#include <mutex>
+
+using namespace ::com::sun::star;
+
+SwMailMergeOutputTypePage::SwMailMergeOutputTypePage(weld::Container* pPage, SwMailMergeWizard* pWizard)
+ : vcl::OWizardPage(pPage, pWizard, "modules/swriter/ui/mmoutputtypepage.ui", "MMOutputTypePage")
+ , m_pWizard(pWizard)
+ , m_xLetterRB(m_xBuilder->weld_radio_button("letter"))
+ , m_xMailRB(m_xBuilder->weld_radio_button("email"))
+ , m_xLetterHint(m_xBuilder->weld_label("letterft"))
+ , m_xMailHint(m_xBuilder->weld_label("emailft"))
+{
+ Link<weld::Toggleable&,void> aLink = LINK(this, SwMailMergeOutputTypePage, TypeHdl_Impl);
+ m_xLetterRB->connect_toggled(aLink);
+ m_xMailRB->connect_toggled(aLink);
+
+ SwMailMergeConfigItem& rConfigItem = m_pWizard->GetConfigItem();
+ if(rConfigItem.IsOutputToLetter())
+ m_xLetterRB->set_active(true);
+ else
+ m_xMailRB->set_active(true);
+ TypeHdl_Impl(*m_xLetterRB);
+}
+
+SwMailMergeOutputTypePage::~SwMailMergeOutputTypePage()
+{
+}
+
+IMPL_LINK_NOARG(SwMailMergeOutputTypePage, TypeHdl_Impl, weld::Toggleable&, void)
+{
+ bool bLetter = m_xLetterRB->get_active();
+ m_xLetterHint->set_visible(bLetter);
+ m_xMailHint->set_visible(!bLetter);
+ m_pWizard->GetConfigItem().SetOutputToLetter(bLetter);
+ m_pWizard->UpdateRoadmap();
+}
+
+struct SwSendMailDialog_Impl
+{
+ friend class SwSendMailDialog;
+ // The mutex is locked in SwSendMailDialog_Impl::GetNextDescriptor, which may be called
+ // both with mutex unlocked (inside SwSendMailDialog::SendMails), and with mutex locked
+ // (inside SwSendMailDialog::AddDocument).
+ std::recursive_mutex aDescriptorMutex;
+
+ std::vector< SwMailDescriptor > aDescriptors;
+ sal_uInt32 nCurrentDescriptor;
+ ::rtl::Reference< MailDispatcher > xMailDispatcher;
+ ::rtl::Reference< IMailDispatcherListener> xMailListener;
+ uno::Reference< mail::XMailService > xConnectedInMailService;
+ Idle aRemoveIdle;
+
+ SwSendMailDialog_Impl() :
+ nCurrentDescriptor(0), aRemoveIdle("SwSendMailDialog_Impl aRemoveIdle")
+ {
+ aRemoveIdle.SetPriority(TaskPriority::LOWEST);
+ }
+
+ ~SwSendMailDialog_Impl()
+ {
+ // Shutdown must be called when the last reference to the
+ // mail dispatcher will be released in order to force a
+ // shutdown of the mail dispatcher thread.
+ // 'join' with the mail dispatcher thread leads to a
+ // deadlock (SolarMutex).
+ if( xMailDispatcher.is() && !xMailDispatcher->isShutdownRequested() )
+ xMailDispatcher->shutdown();
+ }
+ const SwMailDescriptor* GetNextDescriptor();
+};
+
+const SwMailDescriptor* SwSendMailDialog_Impl::GetNextDescriptor()
+{
+ std::scoped_lock aGuard(aDescriptorMutex);
+ if(nCurrentDescriptor < aDescriptors.size())
+ {
+ ++nCurrentDescriptor;
+ return &aDescriptors[nCurrentDescriptor - 1];
+ }
+ return nullptr;
+}
+
+namespace {
+
+class SwMailDispatcherListener_Impl : public IMailDispatcherListener
+{
+ SwSendMailDialog& m_rSendMailDialog;
+
+public:
+ explicit SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg);
+
+ virtual void idle() override;
+ virtual void mailDelivered(uno::Reference< mail::XMailMessage> xMailMessage) override;
+ virtual void mailDeliveryError(::rtl::Reference<MailDispatcher> xMailDispatcher,
+ uno::Reference< mail::XMailMessage> xMailMessage, const OUString& sErrorMessage) override;
+
+ static void DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage );
+};
+
+}
+
+SwMailDispatcherListener_Impl::SwMailDispatcherListener_Impl(SwSendMailDialog& rParentDlg)
+ : m_rSendMailDialog(rParentDlg)
+{
+}
+
+void SwMailDispatcherListener_Impl::idle()
+{
+ SolarMutexGuard aGuard;
+ m_rSendMailDialog.AllMailsSent();
+}
+
+void SwMailDispatcherListener_Impl::mailDelivered(
+ uno::Reference< mail::XMailMessage> xMailMessage)
+{
+ SolarMutexGuard aGuard;
+ m_rSendMailDialog.DocumentSent( xMailMessage, true, nullptr );
+ DeleteAttachments( xMailMessage );
+}
+
+void SwMailDispatcherListener_Impl::mailDeliveryError(
+ ::rtl::Reference<MailDispatcher> /*xMailDispatcher*/,
+ uno::Reference< mail::XMailMessage> xMailMessage,
+ const OUString& sErrorMessage)
+{
+ SolarMutexGuard aGuard;
+ m_rSendMailDialog.DocumentSent( xMailMessage, false, &sErrorMessage );
+ DeleteAttachments( xMailMessage );
+}
+
+void SwMailDispatcherListener_Impl::DeleteAttachments( uno::Reference< mail::XMailMessage > const & xMessage )
+{
+ const uno::Sequence< mail::MailAttachment > aAttachments = xMessage->getAttachments();
+
+ for(const auto& rAttachment : aAttachments)
+ {
+ try
+ {
+ uno::Reference< beans::XPropertySet > xTransferableProperties( rAttachment.Data, uno::UNO_QUERY_THROW);
+ OUString sURL;
+ xTransferableProperties->getPropertyValue("URL") >>= sURL;
+ if(!sURL.isEmpty())
+ SWUnoHelper::UCB_DeleteFile( sURL );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+}
+
+namespace {
+
+class SwSendWarningBox_Impl : public weld::MessageDialogController
+{
+ std::unique_ptr<weld::TextView> m_xDetailED;
+public:
+ SwSendWarningBox_Impl(weld::Window* pParent, const OUString& rDetails)
+ : MessageDialogController(pParent, "modules/swriter/ui/warnemaildialog.ui", "WarnEmailDialog", "grid")
+ , m_xDetailED(m_xBuilder->weld_text_view("errors"))
+ {
+ m_xDetailED->set_size_request(80 * m_xDetailED->get_approximate_digit_width(),
+ 8 * m_xDetailED->get_text_height());
+ m_xDetailED->set_text(rDetails);
+ }
+};
+
+}
+
+SwSendMailDialog::SwSendMailDialog(weld::Window *pParent, SwMailMergeConfigItem& rConfigItem)
+ : GenericDialogController(pParent, "modules/swriter/ui/mmsendmails.ui", "SendMailsDialog")
+ , m_sContinue(SwResId( ST_CONTINUE ))
+ , m_sClose(SwResId(ST_CLOSE_DIALOG))
+ , m_sSendingTo( SwResId(ST_SENDINGTO ))
+ , m_sCompleted( SwResId(ST_COMPLETED ))
+ , m_sFailed( SwResId(ST_FAILED ))
+ , m_sAddressInvalid(SwResId(ST_ADDRESS_INVALID))
+ , m_bCancel(false)
+ , m_bDestructionEnabled(false)
+ , m_pImpl(new SwSendMailDialog_Impl)
+ , m_pConfigItem(&rConfigItem)
+ , m_nExpectedCount(0)
+ , m_nProcessedCount(0)
+ , m_nErrorCount(0)
+ , m_xTransferStatus(m_xBuilder->weld_label("transferstatus"))
+ , m_xPaused(m_xBuilder->weld_label("paused"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+ , m_xErrorStatus(m_xBuilder->weld_label("errorstatus"))
+ , m_xStatus(m_xBuilder->weld_tree_view("container"))
+ , m_xStop(m_xBuilder->weld_button("stop"))
+ , m_xCancel(m_xBuilder->weld_button("cancel"))
+ , m_xExpander(m_xBuilder->weld_expander("details"))
+{
+ m_sStop = m_xStop->get_label();
+ m_sTransferStatus = m_xTransferStatus->get_label();
+ m_sErrorStatus = m_xErrorStatus->get_label();
+
+ Size aSize(m_xStatus->get_approximate_digit_width() * 28,
+ m_xStatus->get_height_rows(20));
+ m_xStatus->set_size_request(aSize.Width(), aSize.Height());
+
+ m_xStop->connect_clicked(LINK( this, SwSendMailDialog, StopHdl_Impl));
+ m_xCancel->connect_clicked(LINK( this, SwSendMailDialog, CancelHdl_Impl));
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xStatus->get_checkbox_column_width()),
+ o3tl::narrowing<int>(aSize.Width()/3 * 2)
+ };
+ m_xStatus->set_column_fixed_widths(aWidths);
+
+ m_xPaused->set_visible(false);
+ UpdateTransferStatus();
+}
+
+SwSendMailDialog::~SwSendMailDialog()
+{
+ if(!m_pImpl->xMailDispatcher.is())
+ return;
+
+ try
+ {
+ if(m_pImpl->xMailDispatcher->isStarted())
+ m_pImpl->xMailDispatcher->stop();
+ if(m_pImpl->xConnectedInMailService.is() && m_pImpl->xConnectedInMailService->isConnected())
+ m_pImpl->xConnectedInMailService->disconnect();
+
+ uno::Reference<mail::XMailMessage> xMessage =
+ m_pImpl->xMailDispatcher->dequeueMailMessage();
+ while(xMessage.is())
+ {
+ SwMailDispatcherListener_Impl::DeleteAttachments( xMessage );
+ xMessage = m_pImpl->xMailDispatcher->dequeueMailMessage();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+void SwSendMailDialog::AddDocument( SwMailDescriptor const & rDesc )
+{
+ std::scoped_lock aGuard(m_pImpl->aDescriptorMutex);
+ m_pImpl->aDescriptors.push_back(rDesc);
+ // if the dialog is already running then continue sending of documents
+ if(m_pImpl->xMailDispatcher.is())
+ {
+ IterateMails();
+ }
+}
+
+IMPL_LINK( SwSendMailDialog, StopHdl_Impl, weld::Button&, rButton, void )
+{
+ m_bCancel = true;
+ if(!m_pImpl->xMailDispatcher.is())
+ return;
+
+ if(m_pImpl->xMailDispatcher->isStarted())
+ {
+ m_pImpl->xMailDispatcher->stop();
+ rButton.set_label(m_sContinue);
+ m_xPaused->show();
+ }
+ else
+ {
+ m_pImpl->xMailDispatcher->start();
+ rButton.set_label(m_sStop);
+ m_xPaused->hide();
+ }
+}
+
+IMPL_LINK_NOARG(SwSendMailDialog, CancelHdl_Impl, weld::Button&, void)
+{
+ m_xDialog->hide();
+
+ if (m_bDestructionEnabled)
+ m_xDialog->response(RET_CANCEL);
+ else
+ {
+ m_pImpl->aRemoveIdle.SetInvokeHandler( LINK( this, SwSendMailDialog, RemoveThis ) );
+ m_pImpl->aRemoveIdle.Start();
+ }
+}
+
+IMPL_STATIC_LINK( SwSendMailDialog, StartSendMails, void*, pDialog, void )
+{
+ static_cast<SwSendMailDialog*>(pDialog)->SendMails();
+}
+
+IMPL_LINK( SwSendMailDialog, RemoveThis, Timer*, pTimer, void )
+{
+ if( m_pImpl->xMailDispatcher.is() )
+ {
+ if(m_pImpl->xMailDispatcher->isStarted())
+ m_pImpl->xMailDispatcher->stop();
+ if(!m_pImpl->xMailDispatcher->isShutdownRequested())
+ m_pImpl->xMailDispatcher->shutdown();
+ }
+
+ if( m_bDestructionEnabled &&
+ (!m_pImpl->xMailDispatcher.is() ||
+ !m_pImpl->xMailDispatcher->isRunning()))
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+ else
+ {
+ pTimer->Start();
+ }
+}
+
+IMPL_STATIC_LINK( SwSendMailDialog, StopSendMails, void*, p, void )
+{
+ SwSendMailDialog* pDialog = static_cast<SwSendMailDialog*>(p);
+ if(pDialog->m_pImpl->xMailDispatcher.is() &&
+ pDialog->m_pImpl->xMailDispatcher->isStarted())
+ {
+ pDialog->m_pImpl->xMailDispatcher->stop();
+ pDialog->m_xStop->set_label(pDialog->m_sContinue);
+ pDialog->m_xPaused->show();
+ }
+}
+
+void SwSendMailDialog::SendMails()
+{
+ if(!m_pConfigItem)
+ {
+ OSL_FAIL("config item not set");
+ return;
+ }
+ auto xWait(std::make_unique<weld::WaitObject>(m_xDialog.get()));
+ //get a mail server connection
+ uno::Reference< mail::XSmtpService > xSmtpServer =
+ SwMailMergeHelper::ConnectToSmtpServer( *m_pConfigItem,
+ m_pImpl->xConnectedInMailService,
+ OUString(), OUString(), m_xDialog.get());
+ bool bIsLoggedIn = xSmtpServer.is() && xSmtpServer->isConnected();
+ xWait.reset();
+ if(!bIsLoggedIn)
+ {
+ OSL_FAIL("create error message");
+ return;
+ }
+ m_pImpl->xMailDispatcher.set( new MailDispatcher(xSmtpServer));
+ IterateMails();
+ m_pImpl->xMailListener = new SwMailDispatcherListener_Impl(*this);
+ m_pImpl->xMailDispatcher->addListener(m_pImpl->xMailListener);
+ if(!m_bCancel)
+ {
+ m_pImpl->xMailDispatcher->start();
+ }
+}
+
+void SwSendMailDialog::IterateMails()
+{
+ const SwMailDescriptor* pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
+ while( pCurrentMailDescriptor )
+ {
+ if (!SwMailMergeHelper::CheckMailAddress( pCurrentMailDescriptor->sEMail))
+ {
+ OUString sMessage = m_sSendingTo;
+ m_xStatus->append();
+ m_xStatus->set_image(m_nProcessedCount, RID_BMP_FORMULA_CANCEL, 0);
+ m_xStatus->set_text(m_nProcessedCount, sMessage.replaceFirst("%1", pCurrentMailDescriptor->sEMail), 1);
+ m_xStatus->set_text(m_nProcessedCount, m_sAddressInvalid, 2);
+ ++m_nProcessedCount;
+ ++m_nErrorCount;
+ UpdateTransferStatus( );
+ pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
+ continue;
+ }
+ rtl::Reference<SwMailMessage> pMessage = new SwMailMessage;
+ if(m_pConfigItem->IsMailReplyTo())
+ pMessage->setReplyToAddress(m_pConfigItem->GetMailReplyTo());
+ pMessage->addRecipient( pCurrentMailDescriptor->sEMail );
+ pMessage->SetSenderName( m_pConfigItem->GetMailDisplayName() );
+ pMessage->SetSenderAddress( m_pConfigItem->GetMailAddress() );
+ if(!pCurrentMailDescriptor->sAttachmentURL.isEmpty())
+ {
+ mail::MailAttachment aAttach;
+ aAttach.Data =
+ new SwMailTransferable(
+ pCurrentMailDescriptor->sAttachmentURL,
+ pCurrentMailDescriptor->sAttachmentName,
+ pCurrentMailDescriptor->sMimeType );
+ aAttach.ReadableName = pCurrentMailDescriptor->sAttachmentName;
+ pMessage->addAttachment( aAttach );
+ }
+ pMessage->setSubject( pCurrentMailDescriptor->sSubject );
+ uno::Reference< datatransfer::XTransferable> xBody =
+ new SwMailTransferable(
+ pCurrentMailDescriptor->sBodyContent,
+ pCurrentMailDescriptor->sBodyMimeType);
+ pMessage->setBody( xBody );
+
+ //CC and BCC are tokenized by ';'
+ if(!pCurrentMailDescriptor->sCC.isEmpty())
+ {
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString sTmp = pCurrentMailDescriptor->sCC.getToken( 0, ';', nPos );
+ if( !sTmp.isEmpty() )
+ pMessage->addCcRecipient( sTmp );
+ }
+ while (nPos >= 0);
+ }
+ if(!pCurrentMailDescriptor->sBCC.isEmpty())
+ {
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString sTmp = pCurrentMailDescriptor->sBCC.getToken( 0, ';', nPos );
+ if( !sTmp.isEmpty() )
+ pMessage->addBccRecipient( sTmp );
+ }
+ while (nPos >= 0);
+ }
+ m_pImpl->xMailDispatcher->enqueueMailMessage( pMessage );
+ pCurrentMailDescriptor = m_pImpl->GetNextDescriptor();
+ }
+ UpdateTransferStatus();
+}
+
+void SwSendMailDialog::StartSend(sal_Int32 nExpectedCount)
+{
+ Application::PostUserEvent( LINK( this, SwSendMailDialog,
+ StartSendMails ), this );
+ m_nExpectedCount = nExpectedCount > 0 ? nExpectedCount : 1;
+}
+
+void SwSendMailDialog::DocumentSent( uno::Reference< mail::XMailMessage> const & xMessage,
+ bool bResult,
+ const OUString* pError )
+{
+ //sending should stop on send errors, except after last error - it will stop in AllMailsSent
+ if (pError && m_nProcessedCount + 1 < m_nExpectedCount &&
+ m_pImpl->xMailDispatcher.is() && m_pImpl->xMailDispatcher->isStarted())
+ {
+ Application::PostUserEvent( LINK( this, SwSendMailDialog,
+ StopSendMails ), this );
+ }
+ OUString sInsertImg(bResult ? OUString(RID_BMP_FORMULA_APPLY) : OUString(RID_BMP_FORMULA_CANCEL));
+
+ OUString sMessage = m_sSendingTo;
+ m_xStatus->append();
+ m_xStatus->set_image(m_nProcessedCount, sInsertImg, 0);
+ m_xStatus->set_text(m_nProcessedCount, sMessage.replaceFirst("%1", xMessage->getRecipients()[0]), 1);
+ m_xStatus->set_text(m_nProcessedCount, bResult ? m_sCompleted : m_sFailed, 2);
+ ++m_nProcessedCount;
+ if(!bResult)
+ ++m_nErrorCount;
+
+ UpdateTransferStatus( );
+
+ if (pError)
+ {
+ SwSendWarningBox_Impl aDlg(m_xDialog.get(), *pError);
+ aDlg.run();
+ }
+}
+
+void SwSendMailDialog::UpdateTransferStatus()
+{
+ OUString sStatus( m_sTransferStatus );
+ sStatus = sStatus.replaceFirst("%1", OUString::number(m_nProcessedCount) );
+ sStatus = sStatus.replaceFirst("%2", OUString::number(m_nExpectedCount));
+ m_xTransferStatus->set_label(sStatus);
+
+ sStatus = m_sErrorStatus.replaceFirst("%1", OUString::number(m_nErrorCount) );
+ m_xErrorStatus->set_label(sStatus);
+
+ if (!m_pImpl->aDescriptors.empty())
+ {
+ assert(m_nExpectedCount && "div-by-zero");
+ m_xProgressBar->set_percentage(m_nProcessedCount * 100 / m_nExpectedCount);
+ }
+ else
+ m_xProgressBar->set_percentage(0);
+}
+
+void SwSendMailDialog::AllMailsSent()
+{
+ if (m_nProcessedCount == m_nExpectedCount)
+ {
+ m_xStop->set_sensitive(false);
+ m_xCancel->set_label(m_sClose);
+ // Leave open if some kind of error occurred
+ if (m_nErrorCount == 0)
+ {
+ m_xDialog->hide();
+ m_xDialog->response(RET_CANCEL);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmoutputtypepage.hxx b/sw/source/ui/dbui/mmoutputtypepage.hxx
new file mode 100644
index 000000000..0fa6c00d0
--- /dev/null
+++ b/sw/source/ui/dbui/mmoutputtypepage.hxx
@@ -0,0 +1,43 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_MMOUTPUTTYPEPAGE_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_MMOUTPUTTYPEPAGE_HXX
+
+#include <vcl/wizardmachine.hxx>
+#include <vcl/weld.hxx>
+class SwMailMergeWizard;
+
+class SwMailMergeOutputTypePage : public vcl::OWizardPage
+{
+ SwMailMergeWizard* m_pWizard;
+
+ std::unique_ptr<weld::RadioButton> m_xLetterRB;
+ std::unique_ptr<weld::RadioButton> m_xMailRB;
+ std::unique_ptr<weld::Label> m_xLetterHint;
+ std::unique_ptr<weld::Label> m_xMailHint;
+
+ DECL_LINK(TypeHdl_Impl, weld::Toggleable&, void);
+
+public:
+ SwMailMergeOutputTypePage(weld::Container* pPage, SwMailMergeWizard* pWizard);
+ virtual ~SwMailMergeOutputTypePage() override;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/mmresultdialogs.cxx b/sw/source/ui/dbui/mmresultdialogs.cxx
new file mode 100644
index 000000000..102920ba5
--- /dev/null
+++ b/sw/source/ui/dbui/mmresultdialogs.cxx
@@ -0,0 +1,1295 @@
+/* -*- 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 <mmresultdialogs.hxx>
+#include <mmconfigitem.hxx>
+#include <mailconfigpage.hxx>
+#include "mmgreetingspage.hxx"
+#include <printdata.hxx>
+#include <swmessdialog.hxx>
+#include <cmdid.h>
+#include <swtypes.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <docsh.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <hintids.hxx>
+#include <swmodule.hxx>
+
+#include <vcl/QueueInfo.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/temporary.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <svtools/ehdl.hxx>
+#include <svtools/sfxecode.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/scheduler.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/urihelper.hxx>
+#include <vcl/print.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+
+#include <unotools/tempfile.hxx>
+#include <osl/file.hxx>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <dbmgr.hxx>
+#include <swunohelper.hxx>
+#include <shellio.hxx>
+#include <svtools/htmlcfg.hxx>
+#include <sfx2/event.hxx>
+#include <swevent.hxx>
+#include <dbui.hxx>
+#include <dbui.hrc>
+#include <doc.hxx>
+#include <sfx2/app.hxx>
+#include <strings.hrc>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <iodetect.hxx>
+
+using namespace svt;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+#define MM_DOCTYPE_OOO 1
+#define MM_DOCTYPE_PDF 2
+#define MM_DOCTYPE_WORD 3
+#define MM_DOCTYPE_HTML 4
+#define MM_DOCTYPE_TEXT 5
+
+static OUString lcl_GetExtensionForDocType(sal_uLong nDocType)
+{
+ OUString sExtension;
+ switch( nDocType )
+ {
+ case MM_DOCTYPE_OOO : sExtension = "odt"; break;
+ case MM_DOCTYPE_PDF : sExtension = "pdf"; break;
+ case MM_DOCTYPE_WORD: sExtension = "doc"; break;
+ case MM_DOCTYPE_HTML: sExtension = "html"; break;
+ case MM_DOCTYPE_TEXT: sExtension = "txt"; break;
+ }
+ return sExtension;
+}
+
+static OUString lcl_GetColumnValueOf(const OUString& rColumn, Reference < container::XNameAccess> const & rxColAccess )
+{
+ OUString sRet;
+ try
+ {
+ if (rxColAccess->hasByName(rColumn))
+ {
+ Any aCol = rxColAccess->getByName(rColumn);
+ Reference< sdb::XColumn > xColumn;
+ aCol >>= xColumn;
+ if(xColumn.is())
+ sRet = xColumn->getString();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return sRet;
+}
+
+/**
+ * Replace email server settings in rConfigItem with those set in Writer's global
+ * mail merge config settings.
+ */
+static void lcl_UpdateEmailSettingsFromGlobalConfig(SwMailMergeConfigItem& rConfigItem)
+{
+ // newly created SwMailMergeConfigItem is initialized with values from (global) config
+ SwMailMergeConfigItem aConfigItem;
+
+ // take over email-related settings
+ rConfigItem.SetMailDisplayName(aConfigItem.GetMailDisplayName());
+ rConfigItem.SetMailAddress(aConfigItem.GetMailAddress());
+ rConfigItem.SetMailReplyTo(aConfigItem.GetMailReplyTo());
+ rConfigItem.SetMailReplyTo(aConfigItem.IsMailReplyTo());
+ rConfigItem.SetMailServer(aConfigItem.GetMailServer());
+ rConfigItem.SetMailPort(aConfigItem.GetMailPort());
+ rConfigItem.SetSecureConnection(aConfigItem.IsSecureConnection());
+ // authentication settings
+ rConfigItem.SetAuthentication(aConfigItem.IsAuthentication());
+ rConfigItem.SetSMTPAfterPOP(aConfigItem.IsSMTPAfterPOP());
+ rConfigItem.SetMailUserName(aConfigItem.GetMailUserName());
+ rConfigItem.SetMailPassword(aConfigItem.GetMailPassword());
+ rConfigItem.SetInServerName(aConfigItem.GetInServerName());
+ rConfigItem.SetInServerPort(aConfigItem.GetInServerPort());
+ rConfigItem.SetInServerPOP(aConfigItem.IsInServerPOP());
+ rConfigItem.SetInServerUserName(aConfigItem.GetInServerUserName());
+ rConfigItem.SetInServerPassword(aConfigItem.GetInServerPassword());
+}
+
+namespace {
+
+class SwSaveWarningBox_Impl : public SwMessageAndEditDialog
+{
+ DECL_LINK( ModifyHdl, weld::Entry&, void);
+public:
+ SwSaveWarningBox_Impl(weld::Window* pParent, const OUString& rFileName);
+
+ OUString GetFileName() const
+ {
+ return m_xEdit->get_text();
+ }
+};
+
+class SwSendQueryBox_Impl : public SwMessageAndEditDialog
+{
+ bool bIsEmptyAllowed;
+ DECL_LINK( ModifyHdl, weld::Entry&, void);
+public:
+ SwSendQueryBox_Impl(weld::Window* pParent, const OString& rID,
+ const OUString& rUIXMLDescription);
+
+ void SetValue(const OUString& rSet)
+ {
+ m_xEdit->set_text(rSet);
+ ModifyHdl(*m_xEdit);
+ }
+
+ OUString GetValue() const
+ {
+ return m_xEdit->get_text();
+ }
+
+ void SetIsEmptyTextAllowed(bool bSet)
+ {
+ bIsEmptyAllowed = bSet;
+ ModifyHdl(*m_xEdit);
+ }
+};
+
+}
+
+SwSaveWarningBox_Impl::SwSaveWarningBox_Impl(weld::Window* pParent, const OUString& rFileName)
+ : SwMessageAndEditDialog(pParent, "AlreadyExistsDialog",
+ "modules/swriter/ui/alreadyexistsdialog.ui")
+{
+ m_xEdit->set_text(rFileName);
+ m_xEdit->connect_changed(LINK(this, SwSaveWarningBox_Impl, ModifyHdl));
+
+ INetURLObject aTmp(rFileName);
+ m_xDialog->set_primary_text(m_xDialog->get_primary_text().replaceAll("%1", aTmp.getName(
+ INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset)));
+
+ ModifyHdl(*m_xEdit);
+}
+
+IMPL_LINK( SwSaveWarningBox_Impl, ModifyHdl, weld::Entry&, rEdit, void)
+{
+ m_xOKPB->set_sensitive(!rEdit.get_text().isEmpty());
+}
+
+SwSendQueryBox_Impl::SwSendQueryBox_Impl(weld::Window* pParent, const OString& rID,
+ const OUString& rUIXMLDescription)
+ : SwMessageAndEditDialog(pParent, rID, rUIXMLDescription)
+ , bIsEmptyAllowed(true)
+{
+ m_xEdit->connect_changed(LINK(this, SwSendQueryBox_Impl, ModifyHdl));
+ ModifyHdl(*m_xEdit);
+}
+
+IMPL_LINK( SwSendQueryBox_Impl, ModifyHdl, weld::Entry&, rEdit, void)
+{
+ m_xOKPB->set_sensitive(bIsEmptyAllowed || !rEdit.get_text().isEmpty());
+}
+
+namespace {
+
+class SwCopyToDialog : public SfxDialogController
+{
+ std::unique_ptr<weld::Entry> m_xCCED;
+ std::unique_ptr<weld::Entry> m_xBCCED;
+
+public:
+ explicit SwCopyToDialog(weld::Window* pParent)
+ : SfxDialogController(pParent, "modules/swriter/ui/ccdialog.ui", "CCDialog")
+ , m_xCCED(m_xBuilder->weld_entry("cc"))
+ , m_xBCCED(m_xBuilder->weld_entry("bcc"))
+ {
+ }
+
+ OUString GetCC() const {return m_xCCED->get_text();}
+ void SetCC(const OUString& rSet) {m_xCCED->set_text(rSet);}
+
+ OUString GetBCC() const {return m_xBCCED->get_text();}
+ void SetBCC(const OUString& rSet) {m_xBCCED->set_text(rSet);}
+};
+
+}
+
+SwMMResultSaveDialog::SwMMResultSaveDialog(weld::Window* pParent)
+ : SfxDialogController(pParent, "modules/swriter/ui/mmresultsavedialog.ui", "MMResultSaveDialog")
+ , m_bCancelSaving(false)
+ , m_xSaveAsOneRB(m_xBuilder->weld_radio_button("singlerb"))
+ , m_xSaveIndividualRB(m_xBuilder->weld_radio_button("individualrb"))
+ , m_xFromRB(m_xBuilder->weld_check_button("fromrb"))
+ , m_xFromNF(m_xBuilder->weld_spin_button("from"))
+ , m_xToFT(m_xBuilder->weld_label("toft"))
+ , m_xToNF(m_xBuilder->weld_spin_button("to"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+{
+ Link<weld::Toggleable&,void> aLink = LINK(this, SwMMResultSaveDialog, DocumentSelectionHdl_Impl);
+ m_xSaveAsOneRB->connect_toggled(aLink);
+ m_xSaveIndividualRB->connect_toggled(aLink);
+ m_xFromRB->connect_toggled(aLink);
+ // m_pSaveAsOneRB is the default, so disable m_xFromNF and m_xToNF initially.
+ aLink.Call(*m_xSaveAsOneRB);
+ if (SwView* pView = GetActiveView())
+ {
+ const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
+ assert(xConfigItem);
+ sal_Int32 nCount = xConfigItem->GetMergedDocumentCount();
+ m_xFromNF->set_max(nCount);
+ m_xToNF->set_max(nCount);
+ m_xToNF->set_value(nCount);
+ }
+
+ m_xOKButton->connect_clicked(LINK(this, SwMMResultSaveDialog, SaveOutputHdl_Impl));
+}
+
+SwMMResultSaveDialog::~SwMMResultSaveDialog()
+{
+}
+
+SwMMResultPrintDialog::SwMMResultPrintDialog(weld::Window* pParent)
+ : SfxDialogController(pParent, "modules/swriter/ui/mmresultprintdialog.ui", "MMResultPrintDialog")
+ , m_xPrinterFT(m_xBuilder->weld_label("printerft"))
+ , m_xPrinterLB(m_xBuilder->weld_combo_box("printers"))
+ , m_xPrinterSettingsPB(m_xBuilder->weld_button("printersettings"))
+ , m_xPrintAllRB(m_xBuilder->weld_radio_button("printallrb"))
+ , m_xFromRB(m_xBuilder->weld_radio_button("fromrb"))
+ , m_xFromNF(m_xBuilder->weld_spin_button("from"))
+ , m_xToFT(m_xBuilder->weld_label("toft"))
+ , m_xToNF(m_xBuilder->weld_spin_button("to"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+{
+ m_xPrinterLB->make_sorted();
+
+ m_xPrinterLB->connect_changed(LINK(this, SwMMResultPrintDialog, PrinterChangeHdl_Impl));
+ m_xPrinterSettingsPB->connect_clicked(LINK(this, SwMMResultPrintDialog, PrinterSetupHdl_Impl));
+
+ Link<weld::Toggleable&,void> aLink = LINK(this, SwMMResultPrintDialog, DocumentSelectionHdl_Impl);
+ m_xPrintAllRB->connect_toggled(aLink);
+ m_xFromRB->connect_toggled(aLink);
+ // m_pPrintAllRB is the default, so disable m_xFromNF and m_xToNF initially.
+ aLink.Call(*m_xPrintAllRB);
+
+ m_xOKButton->connect_clicked(LINK(this, SwMMResultPrintDialog, PrintHdl_Impl));
+
+ FillInPrinterSettings();
+}
+
+SwMMResultPrintDialog::~SwMMResultPrintDialog()
+{
+}
+
+SwMMResultEmailDialog::SwMMResultEmailDialog(weld::Window* pParent)
+ : SfxDialogController(pParent, "modules/swriter/ui/mmresultemaildialog.ui", "MMResultEmailDialog")
+ , m_sConfigureMail(SwResId(ST_CONFIGUREMAIL))
+ , m_xMailToFT(m_xBuilder->weld_label("mailtoft"))
+ , m_xMailToLB(m_xBuilder->weld_combo_box("mailto"))
+ , m_xCopyToPB(m_xBuilder->weld_button("copyto"))
+ , m_xSubjectFT(m_xBuilder->weld_label("subjectft"))
+ , m_xSubjectED(m_xBuilder->weld_entry("subject"))
+ , m_xSendAsFT(m_xBuilder->weld_label("sendasft"))
+ , m_xSendAsLB(m_xBuilder->weld_combo_box("sendas"))
+ , m_xSendAsPB(m_xBuilder->weld_button("sendassettings"))
+ , m_xAttachmentGroup(m_xBuilder->weld_widget("attachgroup"))
+ , m_xAttachmentED(m_xBuilder->weld_entry("attach"))
+ , m_xPasswordFT(m_xBuilder->weld_label("passwordft"))
+ , m_xPasswordLB(m_xBuilder->weld_combo_box("password"))
+ , m_xPasswordCB(m_xBuilder->weld_check_button("passwordcb"))
+ , m_xSendAllRB(m_xBuilder->weld_radio_button("sendallrb"))
+ , m_xFromRB(m_xBuilder->weld_radio_button("fromrb"))
+ , m_xFromNF(m_xBuilder->weld_spin_button("from"))
+ , m_xToFT(m_xBuilder->weld_label("toft"))
+ , m_xToNF(m_xBuilder->weld_spin_button("to"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+{
+ m_xCopyToPB->connect_clicked(LINK(this, SwMMResultEmailDialog, CopyToHdl_Impl));
+ m_xSendAsPB->connect_clicked(LINK(this, SwMMResultEmailDialog, SendAsHdl_Impl));
+ m_xSendAsLB->connect_changed(LINK(this, SwMMResultEmailDialog, SendTypeHdl_Impl));
+ m_xPasswordCB->connect_toggled( LINK( this, SwMMResultEmailDialog, CheckHdl ));
+
+ Link<weld::Toggleable&,void> aLink = LINK(this, SwMMResultEmailDialog, DocumentSelectionHdl_Impl);
+ m_xSendAllRB->connect_toggled(aLink);
+ m_xFromRB->connect_toggled(aLink);
+ // m_xSendAllRB is the default, so disable m_xFromNF and m_xToNF initially.
+ aLink.Call(*m_xSendAllRB);
+
+ m_xOKButton->connect_clicked(LINK(this, SwMMResultEmailDialog, SendDocumentsHdl_Impl));
+
+ m_xPasswordCB->set_sensitive(false);
+ m_xPasswordFT->set_sensitive(false);
+ m_xPasswordLB->set_sensitive(false);
+
+ FillInEmailSettings();
+}
+
+SwMMResultEmailDialog::~SwMMResultEmailDialog()
+{
+}
+
+void SwMMResultPrintDialog::FillInPrinterSettings()
+{
+ //fill printer ListBox
+ SwView* pView = GetActiveView();
+ if (!pView)
+ return;
+ const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
+ const std::vector<OUString>& rPrinters = Printer::GetPrinterQueues();
+ unsigned int nCount = rPrinters.size();
+ bool bMergePrinterExists = false;
+
+ for (unsigned int i = 0; i < nCount; ++i)
+ {
+ m_xPrinterLB->append_text( rPrinters[i] );
+ if( !bMergePrinterExists && rPrinters[i] == xConfigItem->GetSelectedPrinter() )
+ bMergePrinterExists = true;
+ }
+
+ assert(xConfigItem);
+ if(!bMergePrinterExists)
+ {
+ SfxPrinter* pPrinter = pView->GetWrtShell().getIDocumentDeviceAccess().getPrinter( true );
+ m_xPrinterLB->set_active_text(pPrinter->GetName());
+ }
+ else
+ {
+ m_xPrinterLB->set_active_text(xConfigItem->GetSelectedPrinter());
+ }
+ PrinterChangeHdl_Impl(*m_xPrinterLB);
+
+ sal_Int32 count = xConfigItem->GetMergedDocumentCount();
+ m_xFromNF->set_max(count);
+ m_xToNF->set_value(count);
+ m_xToNF->set_max(count);
+}
+
+void SwMMResultEmailDialog::FillInEmailSettings()
+{
+ SwView* pView = GetActiveView();
+ if (!pView)
+ return;
+ const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
+ assert(xConfigItem);
+
+ SwView* pSourceView = xConfigItem->GetSourceView();
+ OSL_ENSURE(pSourceView, "no source view exists");
+ if (pSourceView)
+ {
+ SwDocShell* pDocShell = pSourceView->GetDocShell();
+ if (pDocShell->HasName())
+ {
+ INetURLObject aTmp(pDocShell->GetMedium()->GetName());
+ m_xAttachmentED->set_text(aTmp.getName(
+ INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ));
+ }
+ }
+
+ if (m_xAttachmentED->get_text().isEmpty())
+ {
+ OUString sAttach = "." + lcl_GetExtensionForDocType(m_xSendAsLB->get_active_id().toUInt32());
+ m_xAttachmentED->set_text(sAttach);
+
+ }
+
+ //select first column
+ uno::Reference< sdbcx::XColumnsSupplier > xColsSupp(xConfigItem->GetResultSet(), uno::UNO_QUERY);
+ //get the name of the actual columns
+ uno::Reference < container::XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr;
+ uno::Sequence< OUString > aFields;
+ if (xColAccess.is())
+ aFields = xColAccess->getElementNames();
+
+ // fill mail address and password ListBox
+ assert(m_xMailToLB->get_count() == 0);
+ assert(m_xPasswordLB->get_count() == 0);
+ for (const OUString& rField : std::as_const(aFields))
+ {
+ m_xMailToLB->append_text(rField);
+ m_xPasswordLB->append_text(rField);
+ }
+
+ m_xMailToLB->set_active(0);
+ m_xPasswordLB->set_active(0);
+
+ // then select the right one - may not be available
+ const std::vector<std::pair<OUString, int>>& rHeaders = xConfigItem->GetDefaultAddressHeaders();
+ OUString sEMailColumn = rHeaders[MM_PART_E_MAIL].first;
+ Sequence< OUString> aAssignment = xConfigItem->GetColumnAssignment(xConfigItem->GetCurrentDBData());
+ if (aAssignment.getLength() > MM_PART_E_MAIL && !aAssignment[MM_PART_E_MAIL].isEmpty())
+ sEMailColumn = aAssignment[MM_PART_E_MAIL];
+ if (int pos = m_xMailToLB->find_text(sEMailColumn); pos >= 0)
+ m_xMailToLB->set_active(pos);
+
+ // HTML format pre-selected
+ m_xSendAsLB->set_active(3);
+ SendTypeHdl_Impl(*m_xSendAsLB);
+
+ const sal_Int32 nCount = xConfigItem->GetMergedDocumentCount();
+ m_xFromNF->set_max(nCount);
+ m_xToNF->set_max(nCount);
+ m_xToNF->set_value(nCount);
+}
+
+IMPL_LINK_NOARG(SwMMResultSaveDialog, DocumentSelectionHdl_Impl, weld::Toggleable&, void)
+{
+ bool bEnableFromTo = m_xFromRB->get_active();
+ m_xFromNF->set_sensitive(bEnableFromTo);
+ m_xToFT->set_sensitive(bEnableFromTo);
+ m_xToNF->set_sensitive(bEnableFromTo);
+}
+
+IMPL_LINK_NOARG(SwMMResultEmailDialog, CheckHdl, weld::Toggleable&, void)
+{
+ bool bEnable = m_xPasswordCB->get_active();
+
+ m_xPasswordFT->set_sensitive(bEnable);
+ m_xPasswordLB->set_sensitive(bEnable);
+}
+
+IMPL_LINK_NOARG(SwMMResultPrintDialog, DocumentSelectionHdl_Impl, weld::Toggleable&, void)
+{
+ bool bEnableFromTo = m_xFromRB->get_active();
+ m_xFromNF->set_sensitive(bEnableFromTo);
+ m_xToFT->set_sensitive(bEnableFromTo);
+ m_xToNF->set_sensitive(bEnableFromTo);
+}
+
+IMPL_LINK_NOARG(SwMMResultEmailDialog, DocumentSelectionHdl_Impl, weld::Toggleable&, void)
+{
+ bool bEnableFromTo = m_xFromRB->get_active();
+ m_xFromNF->set_sensitive(bEnableFromTo);
+ m_xToFT->set_sensitive(bEnableFromTo);
+ m_xToNF->set_sensitive(bEnableFromTo);
+}
+
+IMPL_LINK_NOARG(SwMMResultEmailDialog, CopyToHdl_Impl, weld::Button&, void)
+{
+ SwCopyToDialog aDlg(m_xDialog.get());
+ aDlg.SetCC(m_sCC );
+ aDlg.SetBCC(m_sBCC);
+ if (aDlg.run() == RET_OK)
+ {
+ m_sCC = aDlg.GetCC() ;
+ m_sBCC = aDlg.GetBCC();
+ }
+}
+
+namespace {
+
+int documentStartPageNumber(SwMailMergeConfigItem* pConfigItem, int document, bool bIgnoreEmpty)
+{
+ SwView* pTargetView = pConfigItem->GetTargetView();
+ assert( pTargetView );
+ SwCursorShell& shell = pTargetView->GetWrtShell();
+ const SwDocMergeInfo& info = pConfigItem->GetDocumentMergeInfo(document);
+ sal_uInt16 page;
+ shell.Push();
+ shell.GotoMark( info.startPageInTarget );
+ if (!bIgnoreEmpty)
+ shell.GetPageNum(page, o3tl::temporary(sal_uInt16()));
+ else
+ page = shell.GetPageNumSeqNonEmpty();
+ shell.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ return page;
+}
+
+int documentEndPageNumber(SwMailMergeConfigItem* pConfigItem, int document, bool bIgnoreEmpty)
+{
+ SwView* pTargetView = pConfigItem->GetTargetView();
+ assert( pTargetView );
+ SwWrtShell& shell = pTargetView->GetWrtShell();
+ shell.Push();
+ if (document < int(pConfigItem->GetMergedDocumentCount()) - 1)
+ {
+ // Go to the page before the starting page of the next merged document.
+ const SwDocMergeInfo& info = pConfigItem->GetDocumentMergeInfo( document + 1 );
+ shell.GotoMark( info.startPageInTarget );
+ shell.EndPrvPg();
+ }
+ else
+ { // This is the last merged document, so it ends on the page at which the document ends.
+ shell.SttEndDoc( false ); // go to doc end
+ }
+ sal_uInt16 page;
+ if (!bIgnoreEmpty)
+ shell.GetPageNum(page, o3tl::temporary(sal_uInt16()));
+ else
+ page = shell.GetPageNumSeqNonEmpty();
+ shell.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ return page;
+}
+
+} // anonymous namespace
+
+IMPL_LINK_NOARG(SwMMResultSaveDialog, SaveOutputHdl_Impl, weld::Button&, void)
+{
+ SwView* pView = GetActiveView();
+ if (!pView)
+ return;
+ std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
+ assert(xConfigItem);
+
+ const sal_uInt32 nDocumentCount = xConfigItem->GetMergedDocumentCount();
+ sal_uInt32 nBegin = 0;
+ sal_uInt32 nEnd = nDocumentCount;
+
+ if (m_xFromRB->get_active())
+ {
+ nBegin = static_cast<sal_Int32>(m_xFromNF->get_value() - 1);
+ nEnd = static_cast<sal_Int32>(m_xToNF->get_value());
+ if (nEnd > nDocumentCount)
+ nEnd = nDocumentCount;
+ }
+
+ xConfigItem->SetBeginEnd(nBegin, nEnd);
+
+ if (!xConfigItem->GetTargetView())
+ SwDBManager::PerformMailMerge(pView);
+
+ SwView* pTargetView = xConfigItem->GetTargetView();
+ assert(pTargetView);
+
+ OUString sFilter;
+ OUString sPath = SwMailMergeHelper::CallSaveAsDialog(m_xDialog.get(), sFilter);
+ if (sPath.isEmpty())
+ {
+ // just return back to the dialog
+ return;
+ }
+
+ if (m_xSaveAsOneRB->get_active())
+ {
+ uno::Sequence< beans::PropertyValue > aValues { comphelper::makePropertyValue("FilterName", sFilter) };
+
+ uno::Reference< frame::XStorable > xStore( pTargetView->GetDocShell()->GetModel(), uno::UNO_QUERY);
+ ErrCode nErrorCode = ERRCODE_NONE;
+ try
+ {
+ xStore->storeToURL( sPath, aValues );
+ }
+ catch (const task::ErrorCodeIOException& rErrorEx)
+ {
+ nErrorCode = ErrCode(rErrorEx.ErrCode);
+ }
+ catch (const Exception&)
+ {
+ nErrorCode = ERRCODE_IO_GENERAL;
+ }
+ if( nErrorCode != ERRCODE_NONE )
+ {
+ SfxErrorContext aEc(ERRCTX_SFX_SAVEASDOC, pTargetView->GetDocShell()->GetTitle());
+ ErrorHandler::HandleError( nErrorCode );
+ }
+ }
+ else
+ {
+ OUString sTargetTempURL = URIHelper::SmartRel2Abs(
+ INetURLObject(), utl::TempFile::CreateTempName(),
+ URIHelper::GetMaybeFileHdl());
+ std::shared_ptr<const SfxFilter> pSfxFlt = SwIoSystem::GetFilterOfFormat(
+ FILTER_XML,
+ SwDocShell::Factory().GetFilterContainer() );
+
+ uno::Sequence< beans::PropertyValue > aValues(1);
+ beans::PropertyValue* pValues = aValues.getArray();
+ pValues[0].Name = "FilterName";
+ pValues[0].Value <<= pSfxFlt->GetFilterName();
+
+ uno::Reference< frame::XStorable > xStore( pTargetView->GetDocShell()->GetModel(), uno::UNO_QUERY);
+ ErrCode nErrorCode = ERRCODE_NONE;
+ try
+ {
+ xStore->storeToURL( sTargetTempURL, aValues );
+ }
+ catch (const task::ErrorCodeIOException& rErrorEx)
+ {
+ nErrorCode = ErrCode(rErrorEx.ErrCode);
+ }
+ catch (const Exception&)
+ {
+ nErrorCode = ERRCODE_IO_GENERAL;
+ }
+ if( nErrorCode != ERRCODE_NONE )
+ {
+ SfxErrorContext aEc(ERRCTX_SFX_SAVEASDOC, pTargetView->GetDocShell()->GetTitle());
+ ErrorHandler::HandleError( nErrorCode );
+ }
+
+ SwView* pSourceView = xConfigItem->GetSourceView();
+ auto xSaveMonitor = std::make_shared<SaveMonitor>(m_xDialog.get());
+ xSaveMonitor->m_xDocName->set_label(pSourceView->GetDocShell()->GetTitle(22));
+ xSaveMonitor->m_xPrinter->set_label( INetURLObject( sPath ).getFSysPath( FSysStyle::Detect ) );
+ m_bCancelSaving = false;
+ weld::DialogController::runAsync(xSaveMonitor, [this, &xSaveMonitor](sal_Int32 nResult){
+ if (nResult == RET_CANCEL)
+ m_bCancelSaving = true;
+ xSaveMonitor.reset();
+ });
+
+ for(sal_uInt32 nDoc = 0; nDoc < nEnd - nBegin && !m_bCancelSaving; ++nDoc)
+ {
+ INetURLObject aURL(sPath);
+ OUString sExtension = aURL.getExtension();
+ if (sExtension.isEmpty())
+ {
+ sExtension = pSfxFlt->GetWildcard().getGlob().getToken(1, '.');
+ sPath += "." + sExtension;
+ }
+ OUString sStat = SwResId(STR_STATSTR_LETTER) + " " + OUString::number(nDoc + 1);
+ xSaveMonitor->m_xPrintInfo->set_label(sStat);
+
+ //now extract a document from the target document
+ // the shell will be closed at the end, but it is more safe to use SfxObjectShellLock here
+ SfxObjectShellLock xTempDocShell( new SwDocShell( SfxObjectCreateMode::STANDARD ) );
+ xTempDocShell->DoInitNew();
+ SfxViewFrame* pTempFrame = SfxViewFrame::LoadHiddenDocument( *xTempDocShell, SFX_INTERFACE_NONE );
+ SwView* pTempView = static_cast<SwView*>( pTempFrame->GetViewShell() );
+ pTargetView->GetWrtShell().StartAction();
+ SwgReaderOption aOpt;
+ aOpt.SetTextFormats( true );
+ aOpt.SetFrameFormats( true );
+ aOpt.SetPageDescs( true );
+ aOpt.SetNumRules( true );
+ aOpt.SetMerge( false );
+ pTempView->GetDocShell()->LoadStylesFromFile(
+ sTargetTempURL, aOpt, true );
+ pTempView->GetDocShell()->GetDoc()->ReplaceCompatibilityOptions( *pTargetView->GetDocShell()->GetDoc());
+ pTempView->GetDocShell()->GetDoc()->ReplaceDefaults( *pTargetView->GetDocShell()->GetDoc());
+ pTempView->GetDocShell()->GetDoc()->ReplaceDocumentProperties( *pTargetView->GetDocShell()->GetDoc(), true );
+
+ uno::Reference<beans::XPropertySet> const xThisSet(
+ pTargetView->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> const xRetSet(
+ pTempView->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aInteropGrabBag;
+ xThisSet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
+ xRetSet->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag));
+
+ pTargetView->GetWrtShell().PastePages(
+ pTempView->GetWrtShell(), documentStartPageNumber(xConfigItem.get(), nDoc, false),
+ documentEndPageNumber(xConfigItem.get(), nDoc, false));
+ pTargetView->GetWrtShell().EndAction();
+ //then save it
+ OUString sOutPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ OUString sCounter = "_" + OUString::number(nDoc + 1);
+ sOutPath = sOutPath.replaceAt( sOutPath.getLength() - sExtension.getLength() - 1, 0, sCounter);
+
+ while(true)
+ {
+ //time for other slots is needed
+ Scheduler::ProcessEventsToIdle();
+
+ bool bFailed = false;
+ try
+ {
+ pValues[0].Value <<= sFilter;
+ uno::Reference< frame::XStorable > xTempStore( xTempDocShell->GetModel(), uno::UNO_QUERY);
+ xTempStore->storeToURL( sOutPath, aValues );
+ }
+ catch (const uno::Exception&)
+ {
+ bFailed = true;
+ }
+
+ if(bFailed)
+ {
+ std::unique_ptr<SwSaveWarningBox_Impl> xWarning(new SwSaveWarningBox_Impl(m_xDialog.get(), sOutPath));
+ if (RET_OK == xWarning->run())
+ sOutPath = xWarning->GetFileName();
+ else
+ {
+ xTempDocShell->DoClose();
+ m_xDialog->response(RET_OK);
+ return;
+ }
+ }
+ else
+ {
+ xTempDocShell->DoClose();
+ m_xDialog->response(RET_OK);
+ break;
+ }
+ }
+ }
+ if (xSaveMonitor)
+ xSaveMonitor->response(RET_OK);
+ ::osl::File::remove( sTargetTempURL );
+ }
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK(SwMMResultPrintDialog, PrinterChangeHdl_Impl, weld::ComboBox&, rBox, void)
+{
+ SwView* pView = GetActiveView();
+ if (!pView)
+ return;
+ const std::shared_ptr<SwMailMergeConfigItem>& xConfigItem = pView->GetMailMergeConfigItem();
+ assert(xConfigItem);
+ if (rBox.get_active() != -1)
+ {
+ const QueueInfo* pInfo = Printer::GetQueueInfo( rBox.get_active_text(), false );
+
+ if( pInfo )
+ {
+ if ( !m_pTempPrinter )
+ {
+ m_pTempPrinter = VclPtr<Printer>::Create( *pInfo );
+ }
+ else
+ {
+ if( (m_pTempPrinter->GetName() != pInfo->GetPrinterName()) ||
+ (m_pTempPrinter->GetDriverName() != pInfo->GetDriver()) )
+ {
+ m_pTempPrinter.disposeAndClear();
+ m_pTempPrinter = VclPtr<Printer>::Create( *pInfo );
+ }
+ }
+ }
+ else if( ! m_pTempPrinter )
+ m_pTempPrinter = VclPtr<Printer>::Create();
+
+ m_xPrinterSettingsPB->set_sensitive(m_pTempPrinter->HasSupport(PrinterSupport::SetupDialog));
+ }
+ else
+ m_xPrinterSettingsPB->set_sensitive(false);
+
+ xConfigItem->SetSelectedPrinter(rBox.get_active_text());
+}
+
+IMPL_LINK_NOARG(SwMMResultPrintDialog, PrintHdl_Impl, weld::Button&, void)
+{
+ SwView* pView = GetActiveView();
+ if (!pView)
+ return;
+ std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
+ assert(xConfigItem);
+
+ const sal_uInt32 nDocumentCount = xConfigItem->GetMergedDocumentCount();
+ sal_uInt32 nBegin = 0;
+ sal_uInt32 nEnd = nDocumentCount;
+
+ if (m_xFromRB->get_active())
+ {
+ nBegin = static_cast<sal_Int32>(m_xFromNF->get_value() - 1);
+ nEnd = static_cast<sal_Int32>(m_xToNF->get_value());
+ if (nEnd > nDocumentCount)
+ nEnd = nDocumentCount;
+ }
+
+ xConfigItem->SetBeginEnd(nBegin, nEnd);
+
+ if(!xConfigItem->GetTargetView())
+ SwDBManager::PerformMailMerge(pView);
+
+ SwView* pTargetView = xConfigItem->GetTargetView();
+ assert(pTargetView);
+
+ // If we skip autoinserted blanks, then the page numbers used in the print range string
+ // refer to the non-blank pages as they appear in the document (see tdf#89708).
+ const bool bIgnoreEmptyPages =
+ !pTargetView->GetDocShell()->GetDoc()->getIDocumentDeviceAccess().getPrintData().IsPrintEmptyPages();
+ const int nStartPage = documentStartPageNumber(xConfigItem.get(), 0, bIgnoreEmptyPages);
+ const int nEndPage = documentEndPageNumber(xConfigItem.get(), nEnd - nBegin - 1, bIgnoreEmptyPages);
+
+ const OUString sPages(OUString::number(nStartPage) + "-" + OUString::number(nEndPage));
+
+ pTargetView->SetMailMergeConfigItem(xConfigItem);
+ if(m_pTempPrinter)
+ {
+ SfxPrinter *const pDocumentPrinter = pTargetView->GetWrtShell()
+ .getIDocumentDeviceAccess().getPrinter(true);
+ pDocumentPrinter->SetPrinterProps(m_pTempPrinter);
+ // this should be able to handle setting its own printer
+ pTargetView->SetPrinter(pDocumentPrinter);
+ }
+
+ SfxObjectShell* pObjSh = pTargetView->GetViewFrame()->GetObjectShell();
+ SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMerge, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE), pObjSh));
+
+ uno::Sequence aProps{ comphelper::makePropertyValue("MonitorVisible", true),
+ comphelper::makePropertyValue("Pages", sPages) };
+
+ pTargetView->ExecPrint( aProps, false, true );
+ SfxGetpApp()->NotifyEvent(SfxEventHint(SfxEventHintId::SwMailMergeEnd, SwDocShell::GetEventName(STR_SW_EVENT_MAIL_MERGE_END), pObjSh));
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SwMMResultPrintDialog, PrinterSetupHdl_Impl, weld::Button&, void)
+{
+ if (m_pTempPrinter)
+ m_pTempPrinter->Setup(m_xDialog.get());
+}
+
+IMPL_LINK(SwMMResultEmailDialog, SendTypeHdl_Impl, weld::ComboBox&, rBox, void)
+{
+ auto nDocType = rBox.get_active_id().toUInt32();
+ bool bEnable = MM_DOCTYPE_HTML != nDocType && MM_DOCTYPE_TEXT != nDocType;
+ bool bIsPDF = nDocType == MM_DOCTYPE_PDF;
+ m_xSendAsPB->set_sensitive(bEnable);
+ m_xAttachmentGroup->set_sensitive(bEnable);
+ if(bEnable)
+ {
+ //add the correct extension
+ OUString sAttach(m_xAttachmentED->get_text());
+ //do nothing if the user has removed the name - the warning will come early enough
+ if (!sAttach.isEmpty())
+ {
+ sal_Int32 nTokenCount = comphelper::string::getTokenCount(sAttach, '.');
+ if( 2 > nTokenCount)
+ {
+ sAttach += ".";
+ ++nTokenCount;
+ }
+ sAttach = comphelper::string::setToken(sAttach, nTokenCount - 1, '.', lcl_GetExtensionForDocType( nDocType ));
+ m_xAttachmentED->set_text(sAttach);
+ }
+ }
+
+ if(bIsPDF)
+ {
+ m_xPasswordCB->set_sensitive(true);
+ m_xPasswordFT->set_sensitive(true);
+ m_xPasswordLB->set_sensitive(true);
+ CheckHdl(*m_xPasswordCB);
+ }
+ else
+ {
+ m_xPasswordCB->set_sensitive(false);
+ m_xPasswordFT->set_sensitive(false);
+ m_xPasswordLB->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(SwMMResultEmailDialog, SendAsHdl_Impl, weld::Button&, void)
+{
+ SwMailBodyDialog aDlg(m_xDialog.get());
+ aDlg.SetBody(m_sBody);
+ if (RET_OK == aDlg.run())
+ {
+ m_sBody = aDlg.GetBody();
+ }
+}
+
+// Send documents as e-mail
+IMPL_LINK_NOARG(SwMMResultEmailDialog, SendDocumentsHdl_Impl, weld::Button&, void)
+{
+ SwView* pView = GetActiveView();
+ if (!pView)
+ return;
+ std::shared_ptr<SwMailMergeConfigItem> xConfigItem = pView->GetMailMergeConfigItem();
+ assert(xConfigItem);
+
+ const sal_uInt32 nDocumentCount = xConfigItem->GetMergedDocumentCount();
+ sal_uInt32 nBegin = 0;
+ sal_uInt32 nEnd = nDocumentCount;
+
+ if (m_xFromRB->get_active())
+ {
+ nBegin = static_cast<sal_Int32>(m_xFromNF->get_value() - 1);
+ nEnd = static_cast<sal_Int32>(m_xToNF->get_value());
+ if (nEnd > nDocumentCount)
+ nEnd = nDocumentCount;
+ }
+
+ xConfigItem->SetBeginEnd(nBegin, nEnd);
+
+ if (!xConfigItem->GetTargetView())
+ SwDBManager::PerformMailMerge(pView);
+
+ //get the composed document
+ SwView* pTargetView = xConfigItem->GetTargetView();
+ SAL_WARN_IF(!pTargetView, "sw.ui", "No TargetView in SwMailMergeConfigItem");
+
+ if (xConfigItem->GetMailServer().isEmpty() ||
+ !SwMailMergeHelper::CheckMailAddress(xConfigItem->GetMailAddress()) )
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ m_sConfigureMail));
+ xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
+ sal_uInt16 nRet = xQueryBox->run();
+ if (RET_YES == nRet )
+ {
+ SwView* pConfigView = pTargetView ? pTargetView : pView;
+ SfxAllItemSet aSet(pConfigView->GetPool());
+ SwMailConfigDlg aDlg(m_xDialog.get(), aSet);
+ nRet = aDlg.run();
+ }
+
+ if(nRet != RET_OK && nRet != RET_YES)
+ return; // back to the dialog
+
+ // SwMailConfigDlg writes mail merge email settings only to (global) config,
+ // so copy them to the existing config item
+ lcl_UpdateEmailSettingsFromGlobalConfig(*xConfigItem);
+ }
+ //add the documents
+ bool bAsBody = false;
+ rtl_TextEncoding eEncoding = ::osl_getThreadTextEncoding();
+ SfxFilterContainer* pFilterContainer = SwDocShell::Factory().GetFilterContainer();
+ std::shared_ptr<const SfxFilter> pSfxFlt;
+ auto nDocType = m_xSendAsLB->get_active_id().toUInt32();
+ OUString sExtension = lcl_GetExtensionForDocType(nDocType);
+ switch( nDocType )
+ {
+ case MM_DOCTYPE_OOO:
+ {
+ //Make sure we don't pick e.g. the flat xml filter
+ //for this format
+ pSfxFlt = pFilterContainer->GetFilter4FilterName(
+ "writer8",
+ SfxFilterFlags::EXPORT);
+ }
+ break;
+ case MM_DOCTYPE_PDF:
+ {
+ pSfxFlt = pFilterContainer->GetFilter4FilterName(
+ "writer_pdf_Export",
+ SfxFilterFlags::EXPORT);
+ }
+ break;
+ case MM_DOCTYPE_WORD:
+ {
+ //the method SwIOSystemGetFilterOfFormat( ) returns the template filter
+ //because it uses the same user data :-(
+ SfxFilterMatcher aMatcher( pFilterContainer->GetName() );
+ SfxFilterMatcherIter aIter( aMatcher );
+ std::shared_ptr<const SfxFilter> pFilter = aIter.First();
+ while ( pFilter )
+ {
+ if( pFilter->GetUserData() == FILTER_WW8 && pFilter->CanExport() )
+ {
+ pSfxFlt = pFilter;
+ break;
+ }
+ pFilter = aIter.Next();
+ }
+
+ }
+ break;
+ case MM_DOCTYPE_HTML:
+ {
+ bAsBody = true;
+ eEncoding = RTL_TEXTENCODING_UTF8;
+ }
+ break;
+ case MM_DOCTYPE_TEXT:
+ {
+ bAsBody = true;
+ pSfxFlt = pFilterContainer->GetFilter4FilterName(
+ "Text (encoded)", SfxFilterFlags::EXPORT);
+ }
+ break;
+ }
+ if(!pSfxFlt)
+ pSfxFlt = pFilterContainer->GetFilter4Extension(sExtension, SfxFilterFlags::EXPORT);
+
+ if(!pSfxFlt)
+ {
+ m_xDialog->response(RET_OK);
+ return;
+ }
+ OUString sMimeType = pSfxFlt->GetMimeType();
+
+ if (m_xSubjectED->get_text().isEmpty())
+ {
+ std::unique_ptr<SwSendQueryBox_Impl> xQuery(new SwSendQueryBox_Impl(m_xDialog.get(), "SubjectDialog",
+ "modules/swriter/ui/subjectdialog.ui"));
+ xQuery->SetIsEmptyTextAllowed(true);
+ xQuery->SetValue("");
+ if(RET_OK == xQuery->run())
+ {
+ if (!xQuery->GetValue().isEmpty())
+ m_xSubjectED->set_text(xQuery->GetValue());
+ }
+ else
+ return; // back to the dialog
+ }
+ if(!bAsBody && m_xAttachmentED->get_text().isEmpty())
+ {
+ std::unique_ptr<SwSendQueryBox_Impl> xQuery(new SwSendQueryBox_Impl(m_xDialog.get(), "AttachNameDialog",
+ "modules/swriter/ui/attachnamedialog.ui"));
+ xQuery->SetIsEmptyTextAllowed(false);
+ if (RET_OK == xQuery->run())
+ {
+ OUString sAttach(xQuery->GetValue());
+ sal_Int32 nTokenCount = comphelper::string::getTokenCount(sAttach, '.');
+ if (2 > nTokenCount)
+ {
+ sAttach += ".";
+ ++nTokenCount;
+ }
+ sAttach = comphelper::string::setToken(sAttach, nTokenCount - 1, '.', lcl_GetExtensionForDocType(
+ m_xSendAsLB->get_active_id().toUInt32()));
+ m_xAttachmentED->set_text(sAttach);
+ }
+ else
+ return; // back to the dialog
+ }
+
+ OUString sEMailColumn = m_xMailToLB->get_active_text();
+ OSL_ENSURE( !sEMailColumn.isEmpty(), "No email column selected");
+ Reference< sdbcx::XColumnsSupplier > xColsSupp( xConfigItem->GetResultSet(), UNO_QUERY);
+ Reference < container::XNameAccess> xColAccess = xColsSupp.is() ? xColsSupp->getColumns() : nullptr;
+ if(sEMailColumn.isEmpty() || !xColAccess.is() || !xColAccess->hasByName(sEMailColumn))
+ {
+ m_xDialog->response(RET_OK);
+ return;
+ }
+
+ OUString sPasswordColumn = m_xPasswordLB->get_active_text();
+ OSL_ENSURE( !sPasswordColumn.isEmpty(), "No password column selected");
+ if(sPasswordColumn.isEmpty() || !xColAccess.is() || !xColAccess->hasByName(sPasswordColumn))
+ {
+ m_xDialog->response(RET_OK);
+ return;
+ }
+
+ OUString sFilterOptions;
+ if(MM_DOCTYPE_TEXT == nDocType)
+ {
+ SwAsciiOptions aOpt;
+ sal_uInt16 nAppScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+ sal_uInt16 nWhich = GetWhichOfScript( RES_CHRATR_LANGUAGE, nAppScriptType);
+ aOpt.SetLanguage( static_cast<const SvxLanguageItem&>(pTargetView->GetWrtShell().
+ GetDefault( nWhich )).GetLanguage());
+ aOpt.SetParaFlags( LINEEND_CR );
+ aOpt.WriteUserData( sFilterOptions );
+ }
+ else if(MM_DOCTYPE_HTML == nDocType)
+ {
+ sFilterOptions = "EmbedImages";
+ }
+ OUString sTargetTempURL = URIHelper::SmartRel2Abs(
+ INetURLObject(), utl::TempFile::CreateTempName(),
+ URIHelper::GetMaybeFileHdl());
+ std::shared_ptr<const SfxFilter> pTargetSfxFlt = SwIoSystem::GetFilterOfFormat(
+ FILTER_XML,
+ SwDocShell::Factory().GetFilterContainer() );
+
+ uno::Sequence< beans::PropertyValue > aValues { comphelper::makePropertyValue("FilterName", pTargetSfxFlt->GetFilterName()) };
+
+ uno::Reference< frame::XStorable > xStore( pTargetView->GetDocShell()->GetModel(), uno::UNO_QUERY);
+ xStore->storeToURL( sTargetTempURL, aValues );
+
+ //create the send dialog
+ std::shared_ptr<SwSendMailDialog> xDlg = std::make_shared<SwSendMailDialog>(Application::GetDefDialogParent(), *xConfigItem);
+
+ xDlg->StartSend(nEnd - nBegin);
+ weld::DialogController::runAsync(xDlg, [](sal_Int32 /*nResult*/){});
+
+ //help to force painting the dialog
+ //TODO/CLEANUP
+ //predetermined breaking point
+ Application::Reschedule( true );
+ m_xDialog->response(RET_OK);
+ for(sal_uInt32 nDoc = 0; nDoc < nEnd - nBegin; ++nDoc)
+ {
+ SwDocMergeInfo& rInfo = xConfigItem->GetDocumentMergeInfo(nDoc);
+
+ //now extract a document from the target document
+ // the shell will be closed at the end, but it is more safe to use SfxObjectShellLock here
+ SfxObjectShellLock xTempDocShell( new SwDocShell( SfxObjectCreateMode::STANDARD ) );
+ xTempDocShell->DoInitNew();
+ SfxViewFrame* pTempFrame = SfxViewFrame::LoadHiddenDocument( *xTempDocShell, SFX_INTERFACE_NONE );
+ SwView* pTempView = static_cast<SwView*>( pTempFrame->GetViewShell() );
+ pTargetView->GetWrtShell().StartAction();
+ SwgReaderOption aOpt;
+ aOpt.SetTextFormats( true );
+ aOpt.SetFrameFormats( true );
+ aOpt.SetPageDescs( true );
+ aOpt.SetNumRules( true );
+ aOpt.SetMerge( false );
+ pTempView->GetDocShell()->LoadStylesFromFile(
+ sTargetTempURL, aOpt, true );
+ pTempView->GetDocShell()->GetDoc()->ReplaceCompatibilityOptions( *pTargetView->GetDocShell()->GetDoc());
+ pTempView->GetDocShell()->GetDoc()->ReplaceDefaults( *pTargetView->GetDocShell()->GetDoc());
+ pTempView->GetDocShell()->GetDoc()->ReplaceDocumentProperties( *pTargetView->GetDocShell()->GetDoc(), true );
+ pTargetView->GetWrtShell().PastePages(
+ pTempView->GetWrtShell(), documentStartPageNumber(xConfigItem.get(), nDoc, false),
+ documentEndPageNumber(xConfigItem.get(), nDoc, false));
+ pTargetView->GetWrtShell().EndAction();
+
+ //then save it
+ SfxStringItem aName(SID_FILE_NAME,
+ URIHelper::SmartRel2Abs(
+ INetURLObject(), utl::TempFile::CreateTempName(),
+ URIHelper::GetMaybeFileHdl()) );
+
+ {
+ bool withFilterOptions = MM_DOCTYPE_TEXT == nDocType || MM_DOCTYPE_HTML == nDocType;
+ bool withPasswordOptions = m_xPasswordCB->get_active();
+
+ sal_Int32 nTarget = xConfigItem->MoveResultSet(rInfo.nDBRow);
+ OSL_ENSURE( nTarget == rInfo.nDBRow, "row of current document could not be selected");
+ OUString sPassword = lcl_GetColumnValueOf(sPasswordColumn, xColAccess);
+
+ sal_Int32 nOptionCount = (withFilterOptions && withPasswordOptions) ? 4 : withPasswordOptions ? 3 : withFilterOptions ? 2 : 1;
+ sal_Int32 nOpt = 0;
+ uno::Sequence< beans::PropertyValue > aFilterValues(nOptionCount);
+ beans::PropertyValue* pFilterValues = aFilterValues.getArray();
+
+ pFilterValues[nOpt].Name = "FilterName";
+ pFilterValues[nOpt].Value <<= pSfxFlt->GetFilterName();
+
+ if(withFilterOptions)
+ {
+ nOpt++;
+ pFilterValues[nOpt].Name = "FilterOptions";
+ pFilterValues[nOpt].Value <<= sFilterOptions;
+ }
+
+ if(withPasswordOptions)
+ {
+ nOpt++;
+ pFilterValues[nOpt].Name = "EncryptFile";
+ pFilterValues[nOpt].Value <<= true;
+ nOpt++;
+ pFilterValues[nOpt].Name = "DocumentOpenPassword";
+ pFilterValues[nOpt].Value <<= sPassword;
+ }
+
+ uno::Reference< frame::XStorable > xTempStore( pTempView->GetDocShell()->GetModel(), uno::UNO_QUERY);
+ xTempStore->storeToURL( aName.GetValue(), aFilterValues );
+ }
+ xTempDocShell->DoClose();
+
+ sal_Int32 nTarget = xConfigItem->MoveResultSet(rInfo.nDBRow);
+ OSL_ENSURE( nTarget == rInfo.nDBRow, "row of current document could not be selected");
+ OSL_ENSURE( !sEMailColumn.isEmpty(), "No email column selected");
+ OUString sEMail = lcl_GetColumnValueOf(sEMailColumn, xColAccess);
+ SwMailDescriptor aDesc;
+ aDesc.sEMail = sEMail;
+ OUStringBuffer sBody;
+ if(bAsBody)
+ {
+ {
+ //read in the temporary file and use it as mail body
+ SfxMedium aMedium( aName.GetValue(), StreamMode::READ);
+ SvStream* pInStream = aMedium.GetInStream();
+ if(pInStream)
+ pInStream->SetStreamCharSet( eEncoding );
+ else
+ {
+ OSL_FAIL("no output file created?");
+ continue;
+ }
+ OStringBuffer sLine;
+ bool bDone = pInStream->ReadLine( sLine );
+ while ( bDone )
+ {
+ sBody.append( OStringToOUString(sLine, eEncoding) );
+ sBody.append("\n");
+ bDone = pInStream->ReadLine( sLine );
+ }
+ }
+ //remove the temporary file
+ SWUnoHelper::UCB_DeleteFile( aName.GetValue() );
+ }
+ else
+ {
+ sBody = m_sBody;
+ aDesc.sAttachmentURL = aName.GetValue();
+ OUString sAttachment(m_xAttachmentED->get_text());
+ sal_Int32 nTokenCount = comphelper::string::getTokenCount(sAttachment, '.');
+ if (2 > nTokenCount)
+ {
+ sAttachment += ".";
+ sAttachment = comphelper::string::setToken(sAttachment, nTokenCount, '.', sExtension);
+ }
+ else if (o3tl::getToken(sAttachment, nTokenCount - 1, '.') != sExtension)
+ sAttachment += sExtension;
+ aDesc.sAttachmentName = sAttachment;
+ aDesc.sMimeType = sMimeType;
+
+ if (xConfigItem->IsGreetingLine(true))
+ {
+ OUString sNameColumn = xConfigItem->GetAssignedColumn(MM_PART_LASTNAME);
+ OUString sName = lcl_GetColumnValueOf(sNameColumn, xColAccess);
+ OUString sGreeting;
+ if(!sName.isEmpty() && xConfigItem->IsIndividualGreeting(true))
+ {
+ OUString sGenderColumn = xConfigItem->GetAssignedColumn(MM_PART_GENDER);
+ const OUString& sFemaleValue = xConfigItem->GetFemaleGenderValue();
+ OUString sGenderValue = lcl_GetColumnValueOf(sGenderColumn, xColAccess);
+ SwMailMergeConfigItem::Gender eGenderType = sGenderValue == sFemaleValue ?
+ SwMailMergeConfigItem::FEMALE :
+ SwMailMergeConfigItem::MALE;
+
+ sGreeting = SwAddressPreview::FillData(
+ xConfigItem->GetGreetings(eGenderType)
+ [xConfigItem->GetCurrentGreeting(eGenderType)],
+ *xConfigItem);
+ }
+ else
+ {
+ sGreeting =
+ xConfigItem->GetGreetings(SwMailMergeConfigItem::NEUTRAL)
+ [xConfigItem->GetCurrentGreeting(SwMailMergeConfigItem::NEUTRAL)];
+
+ }
+ sGreeting += "\n";
+ sBody.insert(0, sGreeting);
+ }
+ }
+ aDesc.sBodyContent = sBody.makeStringAndClear();
+ if(MM_DOCTYPE_HTML == nDocType)
+ {
+ aDesc.sBodyMimeType = "text/html; charset=" +
+ OUString::createFromAscii(rtl_getBestMimeCharsetFromTextEncoding( eEncoding ));
+ }
+ else
+ aDesc.sBodyMimeType = "text/plain; charset=UTF-8; format=flowed";
+
+ aDesc.sSubject = m_xSubjectED->get_text();
+ aDesc.sCC = m_sCC;
+ aDesc.sBCC = m_sBCC;
+ xDlg->AddDocument( aDesc );
+ //help to force painting the dialog
+ Application::Reschedule( true );
+ //stop creating of data when dialog has been closed
+ if (!xDlg->getDialog()->get_visible())
+ {
+ break;
+ }
+ }
+ xDlg->EnableDestruction();
+ ::osl::File::remove( sTargetTempURL );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/selectdbtabledialog.cxx b/sw/source/ui/dbui/selectdbtabledialog.cxx
new file mode 100644
index 000000000..62f8da5cf
--- /dev/null
+++ b/sw/source/ui/dbui/selectdbtabledialog.cxx
@@ -0,0 +1,143 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <swtypes.hxx>
+#include "selectdbtabledialog.hxx"
+#include "dbtablepreviewdialog.hxx"
+#include <osl/diagnose.h>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+SwSelectDBTableDialog::SwSelectDBTableDialog(weld::Window* pParent,
+ const uno::Reference<sdbc::XConnection>& rConnection)
+ : SfxDialogController(pParent, "modules/swriter/ui/selecttabledialog.ui", "SelectTableDialog")
+ , m_xConnection(rConnection)
+ , m_xTable(m_xBuilder->weld_tree_view("table"))
+ , m_xPreviewPB(m_xBuilder->weld_button("preview"))
+{
+ m_xTable->set_size_request(m_xTable->get_approximate_digit_width() * 60,
+ m_xTable->get_height_rows(6));
+
+ std::vector<int> aWidths{ o3tl::narrowing<int>(m_xTable->get_approximate_digit_width() * 30) };
+ m_xTable->set_column_fixed_widths(aWidths);
+
+ m_xPreviewPB->connect_clicked(LINK(this, SwSelectDBTableDialog, PreviewHdl));
+
+ Reference<XTablesSupplier> xTSupplier(m_xConnection, UNO_QUERY);
+ if (xTSupplier.is())
+ {
+ Reference<XNameAccess> xTables = xTSupplier->getTables();
+ Sequence<OUString> aTables = xTables->getElementNames();
+ const OUString* pTables = aTables.getConstArray();
+ for (int i = 0; i < aTables.getLength(); i++)
+ {
+ OUString sEntry = pTables[i];
+ m_xTable->append_text(sEntry);
+ m_xTable->set_text(i, SwResId(ST_TABLE), 1);
+ }
+ }
+ Reference<XQueriesSupplier> xQSupplier(m_xConnection, UNO_QUERY);
+ if (!xQSupplier.is())
+ return;
+
+ Reference<XNameAccess> xQueries = xQSupplier->getQueries();
+ const Sequence<OUString> aQueries = xQueries->getElementNames();
+ int nPos = m_xTable->n_children();
+ for (const OUString& rQuery : aQueries)
+ {
+ m_xTable->append_text(rQuery);
+ m_xTable->set_text(nPos, SwResId(ST_QUERY), 1);
+ m_xTable->set_id(nPos, OUString::number(1));
+ ++nPos;
+ }
+}
+
+SwSelectDBTableDialog::~SwSelectDBTableDialog() {}
+
+IMPL_LINK_NOARG(SwSelectDBTableDialog, PreviewHdl, weld::Button&, void)
+{
+ int nEntry = m_xTable->get_selected_index();
+ if (nEntry == -1)
+ return;
+
+ OUString sTableOrQuery = m_xTable->get_text(nEntry, 0);
+ sal_Int32 nCommandType = m_xTable->get_id(nEntry).isEmpty() ? 0 : 1;
+
+ OUString sDataSourceName;
+ Reference<XChild> xChild(m_xConnection, UNO_QUERY);
+ if (xChild.is())
+ {
+ Reference<XDataSource> xSource(xChild->getParent(), UNO_QUERY);
+ Reference<XPropertySet> xPrSet(xSource, UNO_QUERY);
+ xPrSet->getPropertyValue("Name") >>= sDataSourceName;
+ }
+ OSL_ENSURE(!sDataSourceName.isEmpty(), "no data source found");
+ Sequence<PropertyValue> aProperties{
+ comphelper::makePropertyValue("DataSourceName", sDataSourceName),
+ comphelper::makePropertyValue("Command", sTableOrQuery),
+ comphelper::makePropertyValue("CommandType", nCommandType),
+ comphelper::makePropertyValue("ShowTreeView", false),
+ comphelper::makePropertyValue("ShowTreeViewButton", false)
+ };
+
+ SwDBTablePreviewDialog aDlg(m_xDialog.get(), aProperties);
+ aDlg.run();
+}
+
+OUString SwSelectDBTableDialog::GetSelectedTable(bool& bIsTable)
+{
+ int nEntry = m_xTable->get_selected_index();
+ if (nEntry != -1)
+ {
+ bIsTable = m_xTable->get_id(nEntry).isEmpty();
+ return m_xTable->get_text(nEntry, 0);
+ }
+ bIsTable = false;
+ return OUString();
+}
+
+void SwSelectDBTableDialog::SetSelectedTable(std::u16string_view rTable, bool bIsTable)
+{
+ for (int i = 0, nCount = m_xTable->n_children(); i < nCount; ++i)
+ {
+ if (m_xTable->get_text(i, 0) == rTable && m_xTable->get_id(i).isEmpty() == bIsTable)
+ {
+ m_xTable->select(i);
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/ui/dbui/selectdbtabledialog.hxx b/sw/source/ui/dbui/selectdbtabledialog.hxx
new file mode 100644
index 000000000..01c1edcc4
--- /dev/null
+++ b/sw/source/ui/dbui/selectdbtabledialog.hxx
@@ -0,0 +1,44 @@
+/* -*- 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_UI_DBUI_SELECTDBTABLEDIALOG_HXX
+#define INCLUDED_SW_SOURCE_UI_DBUI_SELECTDBTABLEDIALOG_HXX
+
+#include <sfx2/basedlgs.hxx>
+
+namespace com::sun::star::sdbc { class XConnection; }
+
+class SwSelectDBTableDialog : public SfxDialogController
+{
+ css::uno::Reference<css::sdbc::XConnection> m_xConnection;
+
+ std::unique_ptr<weld::TreeView> m_xTable;
+ std::unique_ptr<weld::Button> m_xPreviewPB;
+
+ DECL_LINK(PreviewHdl, weld::Button&, void);
+public:
+ SwSelectDBTableDialog(weld::Window* pParent,
+ const css::uno::Reference<css::sdbc::XConnection>& xConnection);
+ virtual ~SwSelectDBTableDialog() override;
+
+ OUString GetSelectedTable(bool& bIsTable);
+ void SetSelectedTable(std::u16string_view rTable, bool bIsTable);
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */