diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/ui/dbui | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/ui/dbui')
23 files changed, 9430 insertions, 0 deletions
diff --git a/sw/source/ui/dbui/addresslistdialog.cxx b/sw/source/ui/dbui/addresslistdialog.cxx new file mode 100644 index 0000000000..9cb433974b --- /dev/null +++ b/sw/source/ui/dbui/addresslistdialog.cxx @@ -0,0 +1,641 @@ +/* -*- 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 <comphelper/diagnose_ex.hxx> +#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 sTmpName; + { + OUString sHomePath(SvtPathOptions().GetWorkPath()); + utl::TempFileNamed aTempFile(sFind, true, u".odb", &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 0000000000..dc0873af00 --- /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 0000000000..e6de5544ce --- /dev/null +++ b/sw/source/ui/dbui/createaddresslistdialog.cxx @@ -0,0 +1,590 @@ +/* -*- 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 <utility> +#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, OUString aURL, SwMailMergeConfigItem const & rConfig) + : SfxDialogController(pParent, "modules/swriter/ui/createaddresslist.ui", "CreateAddressList") + , m_sAddressListFilterName(SwResId(ST_FILTERNAME)) + , m_sURL(std::move(aURL)) + , 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, 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 0000000000..693dffed39 --- /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, OUString aURL, 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 0000000000..e80e75678c --- /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 OUString& 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 0000000000..a683ccb829 --- /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 OUString& 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 0000000000..e6dac3049c --- /dev/null +++ b/sw/source/ui/dbui/dbinsdlg.cxx @@ -0,0 +1,1750 @@ +/* -*- 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 <comphelper/diagnose_ex.hxx> +#include <utility> +#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, + SwDBData aData ) + : SfxDialogController(rView.GetWindow()->GetFrameWeld(), "modules/swriter/ui/insertdbcolumnsdialog.ui", "InsertDbColumnsDialog") + , ConfigItem("Office.Writer/InsertData/DataSet", ConfigItemMode::NONE) + , m_aDBData(std::move(aData)) + , m_sNoTmpl(SwResId(SW_STR_NONE)) + , m_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(); + + m_nGBFormatLen = m_xFormatFrame->get_label().getLength(); + + if (xColSupp.is()) + { + SwWrtShell& rSh = m_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( !m_aDBColumns.insert( std::move(pNew) ).second ) + { + OSL_ENSURE( false, "Spaltenname mehrfach vergeben?" ); + } + } + } + + // fill paragraph templates-ListBox + { + SfxStyleSheetBasePool* pPool = m_pView->GetDocShell()->GetStyleSheetPool(); + m_xLbDbParaColl->append_text( m_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( m_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 < m_aDBColumns.size(); ++n ) + { + const OUString& rS = m_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 = m_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 = m_aDBColumns.find( &aSrch ); + if( it == m_aDBColumns.begin() || (it+1) == m_aDBColumns.end() ) + nInsPos = it - m_aDBColumns.begin(); + else + { + nInsPos = -1; + while( ++it != m_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 < m_aDBColumns.size(); ++n) + m_xLbTableDbColumn->append_text(m_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 = m_pView->GetWrtShell(); + bool bNewSet = false; + if( !m_pTableSet ) + { + bNewSet = true; + m_pTableSet.reset(new SfxItemSet( rSh.GetAttrPool(), SwuiGetUITableAttrRange() )); + + // At first acquire the simple attributes + m_pTableSet->Put( SfxStringItem( FN_PARAM_TABLE_NAME, rSh.GetUniqueTableName() )); + m_pTableSet->Put( SfxUInt16Item( FN_PARAM_TABLE_HEADLINE, 1 ) ); + + m_pTableSet->Put( SfxUInt16Item( SID_BACKGRND_DESTINATION, + rSh.GetViewOptions()->GetTableDest() )); + + SvxBrushItem aBrush( RES_BACKGROUND ); + m_pTableSet->Put( aBrush ); + aBrush.SetWhich(SID_ATTR_BRUSH_ROW); + m_pTableSet->Put( aBrush ); + aBrush.SetWhich(SID_ATTR_BRUSH_TABLE); + m_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 ); + m_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 ); + m_pRep.reset(new SwTableRep( aTabCols )); + m_pRep->SetAlign( text::HoriOrientation::NONE ); + m_pRep->SetSpace( nWidth ); + m_pRep->SetWidth( nWidth ); + m_pRep->SetWidthPercent( 100 ); + m_pTableSet->Put( SwPtrItem( FN_TABLE_REP, m_pRep.get() )); + + m_pTableSet->Put( SfxUInt16Item( SID_HTML_MODE, + ::GetHtmlMode( m_pView->GetDocShell() ))); + } + + sal_Int32 nCols = m_xLbTableCol->n_children(); + if (nCols != m_pRep->GetAllColCount() && nCols > 0) + { + // Number of columns has changed: then the TabCols have to be adjusted + tools::Long nWidth = m_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 ); + } + } + m_pRep.reset(new SwTableRep( aTabCols )); + m_pRep->SetAlign( text::HoriOrientation::NONE ); + m_pRep->SetSpace( nWidth ); + m_pRep->SetWidth( nWidth ); + m_pRep->SetWidthPercent( 100 ); + m_pTableSet->Put( SwPtrItem( FN_TABLE_REP, m_pRep.get() )); + } + + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + ScopedVclPtr<SfxAbstractTabDialog> pDlg(rFact.CreateSwTableTabDlg(m_xDialog.get(), m_pTableSet.get(), &rSh)); + if( RET_OK == pDlg->Execute() ) + m_pTableSet->Put( *pDlg->GetOutputItemSet() ); + else if( bNewSet ) + { + m_pTableSet.reset(); + m_pRep.reset(); + } +} + +IMPL_LINK_NOARG(SwInsertDBColAutoPilot, AutoFormatHdl, weld::Button&, void) +{ + SwAbstractDialogFactory& rFact = swui::GetFactory(); + + ScopedVclPtr<AbstractSwAutoFormatDlg> pDlg(rFact.CreateSwAutoFormatDlg(m_xDialog.get(), m_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 = m_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, m_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 = m_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( std::u16string_view aText, DB_Columns& rColArr ) +{ + size_t nSttPos = 0; + size_t nFndPos; + while( std::u16string_view::npos != ( nFndPos = aText.find( '\x0A', nSttPos )) ) + { + if( 1 < nFndPos ) + { + rColArr.push_back(std::make_unique<DB_Column>(OUString(aText.substr(nSttPos, nFndPos -1)))); + } + rColArr.push_back(std::make_unique<DB_Column>()); + nSttPos = nFndPos + 1; + } + if( nSttPos < aText.size() ) + { + rColArr.push_back(std::make_unique<DB_Column>(OUString(aText.substr(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 = m_aDBColumns.find( &aSrch ); + if( it != m_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.subView( 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 = m_pView->GetWrtShell(); + SwDBFieldType aFieldType( rSh.GetDoc(), aSrch.sColumn, + m_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 = m_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(m_aDBData.sDataSource,m_aDBData.sCommand,m_aDBData.nCommandType,xConnection,m_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::optional<SwWait> oWait; + + 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 = m_aDBColumns.find( &aSrch ); + if (it != m_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( m_pView->GetDocShell() ) & HTMLMODE_ON); + rSh.InsertTable( + pModOpt->GetInsTableFlags(bHTML), + nRows, nCols, (pSelection ? m_xTAutoFormat.get(): nullptr) ); + rSh.MoveTable( GotoPrevTable, fnTableStart ); + + if( pSelection && m_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 ) + oWait.emplace( *m_pView->GetDocShell(), true ); + } + + rSh.MoveTable( GotoCurrTable, fnTableStart ); + if( !pSelection && ( m_pTableSet || m_xTAutoFormat )) + { + if( m_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,SwCursorSkipMode::Chars); + } + + rSh.DoUndo( false ); + + SwTextFormatColl* pColl = nullptr; + { + const OUString sTmplNm(m_xLbDbParaColl->get_active_text()); + if( m_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", m_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 ) + oWait.emplace( *m_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 = m_pView->GetWrtShell(); + const SfxPoolItem* pItem; + + if (m_xTAutoFormat) + { + if (m_xTAutoFormat->IsFrame()) + { + // border is from AutoFormat + m_pTableSet->ClearItem( RES_BOX ); + m_pTableSet->ClearItem( SID_ATTR_BORDER_INNER ); + } + if (m_xTAutoFormat->IsBackground()) + { + m_pTableSet->ClearItem( RES_BACKGROUND ); + m_pTableSet->ClearItem( SID_ATTR_BRUSH_ROW ); + m_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 == m_pTableSet->GetItemState( i, + false, &pItem ) && *pItem == aBrush ) + m_pTableSet->ClearItem( i ); + } + + const SfxStringItem* pTableNameItem = m_pTableSet->GetItemIfSet( FN_PARAM_TABLE_NAME, false); + if( pTableNameItem && pTableNameItem->GetValue() == rSh.GetTableFormat()->GetName() ) + m_pTableSet->ClearItem( FN_PARAM_TABLE_NAME ); + + rSh.MoveTable( GotoCurrTable, fnTableStart ); + rSh.SetMark(); + rSh.MoveTable( GotoCurrTable, fnTableEnd ); + + ItemSetToTableParam( *m_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==m_aDBData.sDataSource && sCommand==m_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 <<= m_aDBData.sDataSource; + pValues[1].Value <<= m_aDBData.sCommand; + pValues[2].Value <<= m_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( m_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 = *m_pView->GetWrtShell().GetNumberFormatter(); + for(size_t nCol = 0; nCol < m_aDBColumns.size(); nCol++) + { + SwInsDBColumn* pColumn = m_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 = *m_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 == m_aDBData.sDataSource && sCommand == m_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(Concat2View(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 < m_aDBColumns.size(); ++nRealColumn) + { + if(m_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 < m_aDBColumns.size() ; ++n ) + { + SwInsDBColumn& rSet = *m_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 0000000000..ec530f9e3c --- /dev/null +++ b/sw/source/ui/dbui/dbtablepreviewdialog.cxx @@ -0,0 +1,119 @@ +/* -*- 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> +#include <utility> + +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(css::uno::Reference<css::frame::XFrame2> xFrame) + : m_xFrame(std::move(xFrame)) + { + } + + 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 0000000000..99315fa017 --- /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 0000000000..7090d5c3e2 --- /dev/null +++ b/sw/source/ui/dbui/mailmergewizard.cxx @@ -0,0 +1,268 @@ +/* -*- 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 <osl/diagnose.h> + +#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 <utility> +#include <view.hxx> + +#include <helpids.h> + +using namespace svt; +using namespace ::com::sun::star; + +SwMailMergeWizard::SwMailMergeWizard(SwView& rView, std::shared_ptr<SwMailMergeConfigItem> xItem) + : RoadmapWizardMachine(rView.GetFrameWeld()) + , m_pSwView(&rView) + , m_bDocumentLoad(false) + , m_xConfigItem(std::move(xItem)) + , 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) +{ + OUString sIdent(OUString::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(OUString::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 0000000000..73811cbf3e --- /dev/null +++ b/sw/source/ui/dbui/mmaddressblockpage.cxx @@ -0,0 +1,1582 @@ +/* -*- 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 <utility> +#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 <comphelper/diagnose_ex.hxx> + +#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 (auto const& aID : RA_SALUTATION) + m_aSalutations.push_back(SwResId(aID)); + for (auto const& aID : RA_PUNCTUATION) + m_aPunctuations.push_back(SwResId(aID)); + 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(Concat2View("<" + 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, + OUString aPreview, + bool bIsAddressBlock) + : SfxDialogController(pParent, "modules/swriter/ui/assignfieldsdialog.ui", "AssignFieldsDialog") + , m_sNone(SwResId(SW_STR_NONE)) + , m_rPreviewString(std::move(aPreview)) + , 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 + { + std::erase(m_aListeners, xListener); + } + + 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(std::move(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 0000000000..f69b446169 --- /dev/null +++ b/sw/source/ui/dbui/mmaddressblockpage.hxx @@ -0,0 +1,280 @@ +/* -*- 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; + + 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, + OUString aPreview, + 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 0000000000..180a6d0441 --- /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 0000000000..d1cdca67d3 --- /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 0000000000..c75a01ee8d --- /dev/null +++ b/sw/source/ui/dbui/mmgreetingspage.cxx @@ -0,0 +1,421 @@ +/* -*- 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 <comphelper/diagnose_ex.hxx> +#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_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 0000000000..cce99a0ffb --- /dev/null +++ b/sw/source/ui/dbui/mmgreetingspage.hxx @@ -0,0 +1,130 @@ +/* -*- 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::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 0000000000..2e01e49ec9 --- /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::TempFileNamed aTempFile( u"", true, sExt ); + aTempFile.EnableKillingFile(); + m_sExampleURL = aTempFile.GetURL(); + } + 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 0000000000..abec12bb2c --- /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 0000000000..0110c0e5d0 --- /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 <osl/diagnose.h> +#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_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 ? RID_BMP_FORMULA_APPLY : 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 0000000000..0fa6c00d0f --- /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 0000000000..d53085c912 --- /dev/null +++ b/sw/source/ui/dbui/mmresultdialogs.cxx @@ -0,0 +1,1296 @@ +/* -*- 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 m_bIsEmptyAllowed; + DECL_LINK( ModifyHdl, weld::Entry&, void); +public: + SwSendQueryBox_Impl(weld::Window* pParent, const OUString& 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) + { + m_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 OUString& rID, + const OUString& rUIXMLDescription) + : SwMessageAndEditDialog(pParent, rID, rUIXMLDescription) + , m_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(m_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_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_xMailToLB(m_xBuilder->weld_combo_box("mailto")) + , m_xCopyToPB(m_xBuilder->weld_button("copyto")) + , m_xSubjectED(m_xBuilder->weld_entry("subject")) + , 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::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) +{ + // work around crash when calling constructor with no active view + if (!GetActiveView()) + { + SAL_WARN("sw", "ignoring SendAs button click, because no active view"); + return; + } + 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() ); + TypedWhichId<SvxLanguageItem> nWhich = GetWhichOfScript( RES_CHRATR_LANGUAGE, nAppScriptType); + const SvxLanguageItem& rDefLangItem = pTargetView->GetWrtShell().GetDefault( nWhich ); + aOpt.SetLanguage( rDefLangItem.GetLanguage() ); + aOpt.SetParaFlags( LINEEND_CR ); + aOpt.WriteUserData( sFilterOptions ); + } + else if(MM_DOCTYPE_HTML == nDocType) + { + sFilterOptions = "EmbedImages"; + } + OUString sTargetTempURL = URIHelper::SmartRel2Abs( + INetURLObject(), utl::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::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) + "\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 0000000000..be5faf3ada --- /dev/null +++ b/sw/source/ui/dbui/selectdbtabledialog.cxx @@ -0,0 +1,144 @@ +/* -*- 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> +#include <utility> + +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, + uno::Reference<sdbc::XConnection> xConnection) + : SfxDialogController(pParent, "modules/swriter/ui/selecttabledialog.ui", "SelectTableDialog") + , m_xConnection(std::move(xConnection)) + , 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 0000000000..aa0bbddea8 --- /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, + 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: */ |