summaryrefslogtreecommitdiffstats
path: root/svtools/source/dialogs/addresstemplate.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svtools/source/dialogs/addresstemplate.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--svtools/source/dialogs/addresstemplate.cxx1122
1 files changed, 1122 insertions, 0 deletions
diff --git a/svtools/source/dialogs/addresstemplate.cxx b/svtools/source/dialogs/addresstemplate.cxx
new file mode 100644
index 000000000..6f29151e3
--- /dev/null
+++ b/svtools/source/dialogs/addresstemplate.cxx
@@ -0,0 +1,1122 @@
+/* -*- 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 <memory>
+#include <svtools/addresstemplate.hxx>
+#include <svtools/strings.hrc>
+#include <svtools/svtresid.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/configitem.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+#include <com/sun/star/util/AliasProgrammaticPair.hpp>
+#include <com/sun/star/ui/dialogs/AddressBookSourcePilot.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbc/SQLWarning.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <svl/filenotation.hxx>
+#include <tools/urlobj.hxx>
+#include <algorithm>
+#include <map>
+#include <set>
+#include <array>
+#include <strings.hxx>
+
+
+namespace svt
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::ui::dialogs;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+ 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 ::comphelper;
+ using namespace ::utl;
+
+ typedef std::set<OUString> StringBag;
+ typedef std::map<OUString, OUString> MapString2String;
+
+ namespace
+ {
+ OUString lcl_getSelectedDataSource( const weld::ComboBox& dataSourceCombo )
+ {
+ OUString selectedDataSource = dataSourceCombo.get_active_text();
+ if (dataSourceCombo.find_text(selectedDataSource) == -1)
+ {
+ // none of the pre-selected entries -> assume a path to a database document
+ OFileNotation aFileNotation( selectedDataSource, OFileNotation::N_SYSTEM );
+ selectedDataSource = aFileNotation.get( OFileNotation::N_URL );
+ }
+ return selectedDataSource;
+ }
+
+ // = IAssignmentData
+
+ class IAssignmentData
+ {
+ public:
+ virtual ~IAssignmentData();
+
+ /// the data source to use for the address book
+ virtual OUString getDatasourceName() const = 0;
+
+ /// the command to use for the address book
+ virtual OUString getCommand() const = 0;
+
+ /// checks whether or not there is an assignment for a given logical field
+ virtual bool hasFieldAssignment(const OUString& _rLogicalName) = 0;
+ /// retrieves the assignment for a given logical field
+ virtual OUString getFieldAssignment(const OUString& _rLogicalName) = 0;
+
+ /// set the assignment for a given logical field
+ virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) = 0;
+
+ virtual void setDatasourceName(const OUString& _rName) = 0;
+ virtual void setCommand(const OUString& _rCommand) = 0;
+ };
+
+ }
+
+ IAssignmentData::~IAssignmentData()
+ {
+ }
+
+
+ // = AssignmentTransientData
+
+ namespace {
+
+ class AssignmentTransientData : public IAssignmentData
+ {
+ protected:
+ OUString m_sDSName;
+ OUString m_sTableName;
+ MapString2String m_aAliases;
+
+ public:
+ AssignmentTransientData(
+ const OUString& _rDataSourceName,
+ const OUString& _rTableName,
+ const Sequence< AliasProgrammaticPair >& _rFields
+ );
+
+ // IAssignmentData overridables
+ virtual OUString getDatasourceName() const override;
+ virtual OUString getCommand() const override;
+
+ virtual bool hasFieldAssignment(const OUString& _rLogicalName) override;
+ virtual OUString getFieldAssignment(const OUString& _rLogicalName) override;
+ virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) override;
+
+ virtual void setDatasourceName(const OUString& _rName) override;
+ virtual void setCommand(const OUString& _rCommand) override;
+ };
+
+ }
+
+ AssignmentTransientData::AssignmentTransientData(
+ const OUString& _rDataSourceName, const OUString& _rTableName,
+ const Sequence< AliasProgrammaticPair >& _rFields )
+ :m_sDSName( _rDataSourceName )
+ ,m_sTableName( _rTableName )
+ {
+ // fill our aliases structure
+ // first collect all known programmatic names
+ StringBag aKnownNames;
+
+ OUString const sLogicalFieldNames(STR_LOGICAL_FIELD_NAMES);
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = sLogicalFieldNames.getToken(0, ';', nIndex);
+ aKnownNames.insert(aToken);
+ }
+ while ( nIndex >= 0);
+
+ // loop through the given names
+ for (const AliasProgrammaticPair& rField : _rFields)
+ {
+ if ( aKnownNames.end() != aKnownNames.find( rField.ProgrammaticName ) )
+ {
+ m_aAliases[ rField.ProgrammaticName ] = rField.Alias;
+ }
+ else
+ {
+ SAL_WARN( "svtools", "AssignmentTransientData::AssignmentTransientData: unknown programmatic name: "
+ << rField.ProgrammaticName );
+ }
+ }
+ }
+
+
+ OUString AssignmentTransientData::getDatasourceName() const
+ {
+ return m_sDSName;
+ }
+
+
+ OUString AssignmentTransientData::getCommand() const
+ {
+ return m_sTableName;
+ }
+
+
+ bool AssignmentTransientData::hasFieldAssignment(const OUString& _rLogicalName)
+ {
+ MapString2String::const_iterator aPos = m_aAliases.find( _rLogicalName );
+ return ( m_aAliases.end() != aPos )
+ && ( !aPos->second.isEmpty() );
+ }
+
+
+ OUString AssignmentTransientData::getFieldAssignment(const OUString& _rLogicalName)
+ {
+ OUString sReturn;
+ MapString2String::const_iterator aPos = m_aAliases.find( _rLogicalName );
+ if ( m_aAliases.end() != aPos )
+ sReturn = aPos->second;
+
+ return sReturn;
+ }
+
+
+ void AssignmentTransientData::setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment)
+ {
+ m_aAliases[ _rLogicalName ] = _rAssignment;
+ }
+
+
+ void AssignmentTransientData::setDatasourceName(const OUString&)
+ {
+ OSL_FAIL( "AssignmentTransientData::setDatasourceName: cannot be implemented for transient data!" );
+ }
+
+
+ void AssignmentTransientData::setCommand(const OUString&)
+ {
+ OSL_FAIL( "AssignmentTransientData::setCommand: cannot be implemented for transient data!" );
+ }
+
+
+ // = AssignmentPersistentData
+
+ namespace {
+
+ class AssignmentPersistentData
+ :public ::utl::ConfigItem
+ ,public IAssignmentData
+ {
+ protected:
+ StringBag m_aStoredFields;
+
+ protected:
+ css::uno::Any getProperty(const OUString& _rLocalName) const;
+
+ OUString getStringProperty(const char* _pLocalName) const;
+ OUString getStringProperty(const OUString& _rLocalName) const;
+
+ void setStringProperty(const char* _pLocalName, const OUString& _rValue);
+
+ public:
+ AssignmentPersistentData();
+
+ // IAssignmentData overridables
+ virtual OUString getDatasourceName() const override;
+ virtual OUString getCommand() const override;
+
+ virtual bool hasFieldAssignment(const OUString& _rLogicalName) override;
+ virtual OUString getFieldAssignment(const OUString& _rLogicalName) override;
+ virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) override;
+
+ virtual void setDatasourceName(const OUString& _rName) override;
+ virtual void setCommand(const OUString& _rCommand) override;
+
+ virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
+
+ private:
+ virtual void ImplCommit() override;
+ void clearFieldAssignment(const OUString& _rLogicalName);
+ };
+
+ }
+
+void AssignmentPersistentData::Notify( const css::uno::Sequence<OUString>& )
+{
+}
+
+void AssignmentPersistentData::ImplCommit()
+{
+}
+
+
+ AssignmentPersistentData::AssignmentPersistentData()
+ :ConfigItem("Office.DataAccess/AddressBook")
+ {
+ const Sequence< OUString > aStoredNames = GetNodeNames("Fields");
+ m_aStoredFields.insert(aStoredNames.begin(), aStoredNames.end());
+ }
+
+ bool AssignmentPersistentData::hasFieldAssignment(const OUString& _rLogicalName)
+ {
+ return (m_aStoredFields.end() != m_aStoredFields.find(_rLogicalName));
+ }
+
+
+ OUString AssignmentPersistentData::getFieldAssignment(const OUString& _rLogicalName)
+ {
+ OUString sAssignment;
+ if (hasFieldAssignment(_rLogicalName))
+ {
+ OUString sFieldPath = "Fields/" + _rLogicalName + "/AssignedFieldName";
+ sAssignment = getStringProperty(sFieldPath);
+ }
+ return sAssignment;
+ }
+
+
+ Any AssignmentPersistentData::getProperty(const OUString& _rLocalName) const
+ {
+ Sequence< OUString > aProperties(&_rLocalName, 1);
+ Sequence< Any > aValues = const_cast<AssignmentPersistentData*>(this)->GetProperties(aProperties);
+ DBG_ASSERT(aValues.getLength() == 1, "AssignmentPersistentData::getProperty: invalid sequence length!");
+ return aValues[0];
+ }
+
+
+ OUString AssignmentPersistentData::getStringProperty(const OUString& _rLocalName) const
+ {
+ OUString sReturn;
+ getProperty( _rLocalName ) >>= sReturn;
+ return sReturn;
+ }
+
+
+ OUString AssignmentPersistentData::getStringProperty(const char* _pLocalName) const
+ {
+ OUString sReturn;
+ getProperty(OUString::createFromAscii(_pLocalName)) >>= sReturn;
+ return sReturn;
+ }
+
+
+ void AssignmentPersistentData::setStringProperty(const char* _pLocalName, const OUString& _rValue)
+ {
+ Sequence< OUString > aNames { OUString::createFromAscii(_pLocalName) };
+ Sequence< Any > aValues{ Any(_rValue) };
+ PutProperties(aNames, aValues);
+ }
+
+
+ void AssignmentPersistentData::setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment)
+ {
+ if (_rAssignment.isEmpty())
+ {
+ if (hasFieldAssignment(_rLogicalName))
+ {
+ // the assignment exists but it should be reset
+ clearFieldAssignment(_rLogicalName);
+ }
+ return;
+ }
+
+ // Fields
+ OUString sDescriptionNodePath("Fields");
+
+ // Fields/<field>
+ OUString sFieldElementNodePath = sDescriptionNodePath + "/" + _rLogicalName;
+
+ Sequence< PropertyValue > aNewFieldDescription{
+ // Fields/<field>/ProgrammaticFieldName
+ comphelper::makePropertyValue(sFieldElementNodePath + "/ProgrammaticFieldName",
+ _rLogicalName),
+ // Fields/<field>/AssignedFieldName
+ comphelper::makePropertyValue(sFieldElementNodePath + "/AssignedFieldName",
+ _rAssignment)
+ };
+
+ // just set the new value
+ bool bSuccess =
+ SetSetProperties(sDescriptionNodePath, aNewFieldDescription);
+ DBG_ASSERT(bSuccess, "AssignmentPersistentData::setFieldAssignment: could not commit the changes a field!");
+ }
+
+
+ void AssignmentPersistentData::clearFieldAssignment(const OUString& _rLogicalName)
+ {
+ if (!hasFieldAssignment(_rLogicalName))
+ // nothing to do
+ return;
+
+ Sequence< OUString > aNames(&_rLogicalName, 1);
+ ClearNodeElements("Fields", aNames);
+ }
+
+
+ OUString AssignmentPersistentData::getDatasourceName() const
+ {
+ return getStringProperty( "DataSourceName" );
+ }
+
+
+ OUString AssignmentPersistentData::getCommand() const
+ {
+ return getStringProperty( "Command" );
+ }
+
+
+ void AssignmentPersistentData::setDatasourceName(const OUString& _rName)
+ {
+ setStringProperty( "DataSourceName", _rName );
+ }
+
+
+ void AssignmentPersistentData::setCommand(const OUString& _rCommand)
+ {
+ setStringProperty( "Command", _rCommand );
+ }
+
+
+ // = AddressBookSourceDialogData
+
+ struct AddressBookSourceDialogData
+ {
+ std::array<std::unique_ptr<weld::Label>, FIELD_PAIRS_VISIBLE*2> pFieldLabels;
+ std::array<std::unique_ptr<weld::ComboBox>, FIELD_PAIRS_VISIBLE*2> pFields;
+
+ /// when working transient, we need the data source
+ Reference< XDataSource >
+ m_xTransientDataSource;
+ /// current scroll pos in the field list
+ sal_Int32 nFieldScrollPos;
+ /// indicates that we've an odd field number. This member is for efficiency only, it's redundant.
+ bool bOddFieldNumber : 1;
+ /// indicates that we're working with the real persistent configuration
+ bool bWorkingPersistent : 1;
+
+ /// the strings to use as labels for the field selection listboxes
+ std::vector<OUString> aFieldLabels;
+ // the current field assignment
+ std::vector<OUString> aFieldAssignments;
+ /// the logical field names
+ std::vector<OUString> aLogicalFieldNames;
+
+ std::unique_ptr<IAssignmentData> pConfigData;
+
+
+ AddressBookSourceDialogData( )
+ :pFieldLabels{{nullptr}}
+ ,pFields{{nullptr}}
+ ,nFieldScrollPos(0)
+ ,bOddFieldNumber(false)
+ ,bWorkingPersistent( true )
+ ,pConfigData( new AssignmentPersistentData )
+ {
+ }
+
+ AddressBookSourceDialogData( const Reference< XDataSource >& _rxTransientDS, const OUString& _rDataSourceName,
+ const OUString& _rTableName, const Sequence< AliasProgrammaticPair >& _rFields )
+ :pFieldLabels{{nullptr}}
+ ,pFields{{nullptr}}
+ ,m_xTransientDataSource( _rxTransientDS )
+ ,nFieldScrollPos(0)
+ ,bOddFieldNumber(false)
+ ,bWorkingPersistent( false )
+ ,pConfigData( new AssignmentTransientData( _rDataSourceName, _rTableName, _rFields ) )
+ {
+ }
+
+ // Copy assignment is forbidden and not implemented.
+ AddressBookSourceDialogData (const AddressBookSourceDialogData &) = delete;
+ AddressBookSourceDialogData & operator= (const AddressBookSourceDialogData &) = delete;
+ };
+
+ // = AddressBookSourceDialog
+ AddressBookSourceDialog::AddressBookSourceDialog(weld::Window* pParent,
+ const Reference< XComponentContext >& _rxORB )
+ : GenericDialogController(pParent, "svt/ui/addresstemplatedialog.ui", "AddressTemplateDialog")
+ , m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))
+ , m_xORB(_rxORB)
+ , m_pImpl( new AddressBookSourceDialogData )
+ {
+ implConstruct();
+ }
+
+ AddressBookSourceDialog::AddressBookSourceDialog(weld::Window* pParent, const Reference< XComponentContext >& _rxORB,
+ const Reference< XDataSource >& _rxTransientDS, const OUString& _rDataSourceName,
+ const OUString& _rTable, const Sequence< AliasProgrammaticPair >& _rMapping )
+ : GenericDialogController(pParent, "svt/ui/addresstemplatedialog.ui", "AddressTemplateDialog")
+ , m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))
+ , m_xORB(_rxORB)
+ , m_pImpl( new AddressBookSourceDialogData( _rxTransientDS, _rDataSourceName, _rTable, _rMapping ) )
+ {
+ implConstruct();
+ }
+
+ void AddressBookSourceDialog::implConstruct()
+ {
+ m_xOKButton = m_xBuilder->weld_button("ok");
+ m_xDatasource = m_xBuilder->weld_combo_box("datasource");
+ m_xAdministrateDatasources = m_xBuilder->weld_button("admin");
+ m_xTable = m_xBuilder->weld_combo_box("datatable");
+ m_xFieldScroller = m_xBuilder->weld_scrolled_window("scrollwindow", true);
+ m_xGrid = m_xBuilder->weld_widget("grid");
+
+ for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
+ {
+ for (sal_Int32 column=0; column<2; ++column)
+ {
+ // the label
+ m_pImpl->pFieldLabels[row * 2 + column] = m_xBuilder->weld_label("label" + OString::number(row * 2 + column));
+ // the listbox
+ m_pImpl->pFields[row * 2 + column] = m_xBuilder->weld_combo_box("box" + OString::number(row * 2 + column));
+ m_pImpl->pFields[row * 2 + column]->connect_changed(LINK(this, AddressBookSourceDialog, OnFieldSelect));
+ }
+ }
+
+ initializeDatasources();
+
+ // for the moment, we have a hard coded list of all known fields.
+ // A better solution would be to store all known field translations in the configuration, which could be
+ // extensible by the user in an arbitrary way.
+ // But for the moment we need a quick solution ...
+ // (the main thing would be to store the translations to use here in the user interface, besides that, the code
+ // should be adjustable with a rather small effort.)
+
+ // initialize the strings for the field labels
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_FIRSTNAME ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_LASTNAME ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_COMPANY));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_DEPARTMENT ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_STREET ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ZIPCODE ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_CITY ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_STATE));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_COUNTRY ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_HOMETEL ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_WORKTEL ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_OFFICETEL));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_MOBILE));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_TELOTHER));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_PAGER));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_FAX ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_EMAIL ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_URL ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_TITLE ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_POSITION ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_INITIALS ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ADDRFORM ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_SALUTATION ));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ID));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_CALENDAR));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_INVITE));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_NOTE));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER1));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER2));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER3));
+ m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER4));
+
+ tools::Long nLabelWidth = 0;
+ tools::Long nListBoxWidth = m_pImpl->pFields[0]->get_approximate_digit_width() * 18;
+ for (auto const& fieldLabel : m_pImpl->aFieldLabels)
+ {
+ m_pImpl->pFieldLabels[0]->set_label(fieldLabel);
+ nLabelWidth = std::max(nLabelWidth, m_pImpl->pFieldLabels[0]->get_preferred_size().Width());
+ }
+ for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
+ {
+ for (sal_Int32 column=0; column<2; ++column)
+ {
+ m_pImpl->pFieldLabels[row * 2 + column]->set_size_request(nLabelWidth, -1);
+ m_pImpl->pFields[row * 2 + column]->set_size_request(nListBoxWidth, -1);
+ }
+ }
+
+ // force an even number of known fields
+ m_pImpl->bOddFieldNumber = (m_pImpl->aFieldLabels.size() % 2) != 0;
+ if (m_pImpl->bOddFieldNumber)
+ m_pImpl->aFieldLabels.emplace_back();
+
+ // limit the scrollbar range accordingly
+ sal_Int32 nOverallFieldPairs = m_pImpl->aFieldLabels.size() / 2;
+ m_xFieldScroller->vadjustment_configure(0, 0, nOverallFieldPairs,
+ 1, FIELD_PAIRS_VISIBLE - 1, FIELD_PAIRS_VISIBLE);
+
+ // reset the current field assignments
+ m_pImpl->aFieldAssignments.resize(m_pImpl->aFieldLabels.size());
+ // (empty strings mean "no assignment")
+
+ // some knittings
+ m_xFieldScroller->connect_vadjustment_changed(LINK(this, AddressBookSourceDialog, OnFieldScroll));
+ m_xAdministrateDatasources->connect_clicked(LINK(this, AddressBookSourceDialog, OnAdministrateDatasources));
+ m_xDatasource->set_entry_completion(true);
+ m_xTable->set_entry_completion(true);
+ m_xTable->connect_focus_in(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
+ m_xDatasource->connect_focus_in(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
+ m_xTable->connect_focus_out(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
+ m_xDatasource->connect_focus_out(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
+ m_xTable->connect_changed(LINK(this, AddressBookSourceDialog, OnComboSelect));
+ m_xDatasource->connect_changed(LINK(this, AddressBookSourceDialog, OnComboSelect));
+ m_xOKButton->connect_clicked(LINK(this, AddressBookSourceDialog, OnOkClicked));
+
+ // initialize the field controls
+ resetFields();
+ // tdf#136494 wait until contents are filled before getting preferred height
+ m_xFieldScroller->set_size_request(-1, m_xGrid->get_preferred_size().Height());
+ m_xFieldScroller->vadjustment_set_value(0);
+ m_pImpl->nFieldScrollPos = -1;
+ implScrollFields(0, false, false);
+
+ // the logical names
+ OUString sLogicalFieldNames(STR_LOGICAL_FIELD_NAMES);
+ sal_Int32 nAdjustedTokenCount = comphelper::string::getTokenCount(sLogicalFieldNames, ';') + (m_pImpl->bOddFieldNumber ? 1 : 0);
+ DBG_ASSERT(nAdjustedTokenCount == static_cast<sal_Int32>(m_pImpl->aFieldLabels.size()),
+ "AddressBookSourceDialog::AddressBookSourceDialog: inconsistence between logical and UI field names!");
+ m_pImpl->aLogicalFieldNames.reserve(nAdjustedTokenCount);
+ sal_Int32 nIdx{ 0 };
+ for (sal_Int32 i = 0; i<nAdjustedTokenCount; ++i)
+ m_pImpl->aLogicalFieldNames.push_back(sLogicalFieldNames.getToken(0, ';', nIdx));
+
+ Application::PostUserEvent(LINK(this, AddressBookSourceDialog, OnDelayedInitialize), nullptr, false);
+
+ // so the dialog will at least show up before we do the loading of the
+ // configuration data and the (maybe time consuming) analysis of the data source/table to select
+
+ if (m_pImpl->bWorkingPersistent)
+ return;
+
+ m_xAdministrateDatasources->hide();
+ }
+
+ void AddressBookSourceDialog::getFieldMapping(Sequence< AliasProgrammaticPair >& _rMapping) const
+ {
+ _rMapping.realloc( m_pImpl->aLogicalFieldNames.size() );
+ AliasProgrammaticPair* pPair = _rMapping.getArray();
+
+ for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
+ {
+ if ( m_pImpl->pConfigData->hasFieldAssignment(logicalFieldName) )
+ {
+ // the user gave us an assignment for this field
+ pPair->ProgrammaticName = logicalFieldName;
+ pPair->Alias = m_pImpl->pConfigData->getFieldAssignment(logicalFieldName);
+ ++pPair;
+ }
+ }
+
+ _rMapping.realloc( pPair - _rMapping.getArray() );
+ }
+
+ void AddressBookSourceDialog::loadConfiguration()
+ {
+ OUString sName = m_pImpl->pConfigData->getDatasourceName();
+ INetURLObject aURL( sName );
+ if( aURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ sName = aFileNotation.get(OFileNotation::N_SYSTEM);
+ }
+
+ m_xDatasource->set_entry_text(sName);
+ m_xTable->set_entry_text(m_pImpl->pConfigData->getCommand());
+ // we ignore the CommandType: only tables are supported
+
+ // the logical names for the fields
+ // AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!
+ assert(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size());
+
+ auto aAssignment = m_pImpl->aFieldAssignments.begin();
+ for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
+ {
+ *aAssignment = m_pImpl->pConfigData->getFieldAssignment(logicalFieldName);
+ ++aAssignment;
+ }
+ }
+
+ AddressBookSourceDialog::~AddressBookSourceDialog()
+ {
+ }
+
+ void AddressBookSourceDialog::initializeDatasources()
+ {
+ if (!m_xDatabaseContext.is())
+ {
+ DBG_ASSERT(m_xORB.is(), "AddressBookSourceDialog::initializeDatasources: no service factory!");
+ if (!m_xORB.is())
+ return;
+
+ try
+ {
+ m_xDatabaseContext = DatabaseContext::create(m_xORB);
+ }
+ catch(const Exception&) { }
+ if (!m_xDatabaseContext.is())
+ {
+ ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.sdb.DatabaseContext", false);
+ return;
+ }
+ }
+ m_xDatasource->clear();
+
+ // fill the datasources listbox
+ try
+ {
+ const css::uno::Sequence<OUString> aElementNames = m_xDatabaseContext->getElementNames();
+ for (const OUString& rDatasourceName : aElementNames)
+ m_xDatasource->append_text(rDatasourceName);
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svtools", "AddressBookSourceDialog::initializeDatasources: caught an exception while asking for the data source names!");
+ }
+ }
+
+ IMPL_LINK(AddressBookSourceDialog, OnFieldScroll, weld::ScrolledWindow&, rScrollBar, void)
+ {
+ implScrollFields(rScrollBar.vadjustment_get_value(), true, true);
+ }
+
+ void AddressBookSourceDialog::resetTables()
+ {
+ if (!m_xDatabaseContext.is())
+ return;
+
+ weld::WaitObject aWaitCursor(m_xDialog.get());
+
+ // no matter what we do here, we handled the currently selected data source (no matter if successful or not)
+ m_xDatasource->save_value();
+
+ // create an interaction handler (may be needed for connecting)
+ Reference< XInteractionHandler > xHandler;
+ try
+ {
+ xHandler.set(
+ InteractionHandler::createWithParent(m_xORB, m_xDialog->GetXWindow()),
+ UNO_QUERY_THROW );
+ }
+ catch(const Exception&) { }
+ if (!xHandler.is())
+ {
+ ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.task.InteractionHandler", true);
+ return;
+ }
+
+ // the currently selected table
+ OUString sOldTable = m_xTable->get_active_text();
+
+ m_xTable->clear();
+
+ m_xCurrentDatasourceTables= nullptr;
+
+ // get the tables of the connection
+ Sequence< OUString > aTableNames;
+ Any aException;
+ try
+ {
+ Reference< XCompletedConnection > xDS;
+ if ( m_pImpl->bWorkingPersistent )
+ {
+ OUString sSelectedDS = lcl_getSelectedDataSource(*m_xDatasource);
+
+ // get the data source the user has chosen and let it build a connection
+ INetURLObject aURL( sSelectedDS );
+ if ( aURL.GetProtocol() != INetProtocol::NotValid || m_xDatabaseContext->hasByName(sSelectedDS) )
+ m_xDatabaseContext->getByName( sSelectedDS ) >>= xDS;
+ }
+ else
+ {
+ xDS.set(m_pImpl->m_xTransientDataSource, css::uno::UNO_QUERY);
+ }
+
+ // build the connection
+ Reference< XConnection > xConn;
+ if (xDS.is())
+ xConn = xDS->connectWithCompletion(xHandler);
+
+ // get the table names
+ Reference< XTablesSupplier > xSupplTables(xConn, UNO_QUERY);
+ if (xSupplTables.is())
+ {
+ m_xCurrentDatasourceTables = xSupplTables->getTables();
+ if (m_xCurrentDatasourceTables.is())
+ aTableNames = m_xCurrentDatasourceTables->getElementNames();
+ }
+ }
+ catch(const SQLContext& e) { aException <<= e; }
+ catch(const SQLWarning& e) { aException <<= e; }
+ catch(const SQLException& e) { aException <<= e; }
+ catch(Exception&)
+ {
+ OSL_FAIL("AddressBookSourceDialog::resetTables: could not retrieve the table!");
+ }
+
+ if (aException.hasValue())
+ {
+ Reference< XInteractionRequest > xRequest = new OInteractionRequest(aException);
+ try
+ {
+ xHandler->handle(xRequest);
+ }
+ catch(Exception&) { }
+ return;
+ }
+
+ bool bKnowOldTable = false;
+ // fill the table list
+ for (const OUString& rTableName : std::as_const(aTableNames))
+ {
+ m_xTable->append_text(rTableName);
+ if (rTableName == sOldTable)
+ bKnowOldTable = true;
+ }
+
+ // set the old table, if the new data source knows a table with this name, too. Else reset the tables edit field.
+ if (!bKnowOldTable)
+ sOldTable.clear();
+ m_xTable->set_entry_text(sOldTable);
+
+ resetFields();
+ }
+
+ void AddressBookSourceDialog::resetFields()
+ {
+ weld::WaitObject aWaitCursor(m_xDialog.get());
+
+ // no matter what we do here, we handled the currently selected table (no matter if successful or not)
+ m_xDatasource->save_value();
+
+ OUString sSelectedTable = m_xTable->get_active_text();
+ Sequence< OUString > aColumnNames;
+ try
+ {
+ if (m_xCurrentDatasourceTables.is())
+ {
+ // get the table and the columns
+ Reference< XColumnsSupplier > xSuppTableCols;
+ if (m_xCurrentDatasourceTables->hasByName(sSelectedTable))
+ xSuppTableCols.set(
+ m_xCurrentDatasourceTables->getByName(sSelectedTable),
+ css::uno::UNO_QUERY);
+ Reference< XNameAccess > xColumns;
+ if (xSuppTableCols.is())
+ xColumns = xSuppTableCols->getColumns();
+ if (xColumns.is())
+ aColumnNames = xColumns->getElementNames();
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("AddressBookSourceDialog::resetFields: could not retrieve the table columns!");
+ }
+
+
+ // for quicker access
+ ::std::set< OUString > aColumnNameSet(std::cbegin(aColumnNames), std::cend(aColumnNames));
+
+ std::vector<OUString>::iterator aInitialSelection = m_pImpl->aFieldAssignments.begin() + m_pImpl->nFieldScrollPos;
+
+ OUString sSaveSelection;
+ for (sal_Int32 i=0; i<FIELD_CONTROLS_VISIBLE; ++i, ++aInitialSelection)
+ {
+ weld::ComboBox* pListbox = m_pImpl->pFields[i].get();
+ sSaveSelection = pListbox->get_active_text();
+
+ pListbox->clear();
+
+ // the one entry for "no selection"
+ pListbox->append_text(m_sNoFieldSelection);
+ // as it's entry data, set the index of the list box in our array
+ pListbox->set_id(0, OUString::number(i));
+
+ // the field names
+ for (const OUString& rColumnName : std::as_const(aColumnNames))
+ pListbox->append_text(rColumnName);
+
+ if (!aInitialSelection->isEmpty() && (aColumnNameSet.end() != aColumnNameSet.find(*aInitialSelection)))
+ // we can select the entry as specified in our field assignment array
+ pListbox->set_active_text(*aInitialSelection);
+ else
+ // try to restore the selection
+ if (aColumnNameSet.end() != aColumnNameSet.find(sSaveSelection))
+ // the old selection is a valid column name
+ pListbox->set_active_text(sSaveSelection);
+ else
+ // select the <none> entry
+ pListbox->set_active(0);
+ }
+
+ // adjust m_pImpl->aFieldAssignments
+ for (auto & fieldAssignment : m_pImpl->aFieldAssignments)
+ if (!fieldAssignment.isEmpty())
+ if (aColumnNameSet.end() == aColumnNameSet.find(fieldAssignment))
+ fieldAssignment.clear();
+ }
+
+ IMPL_LINK(AddressBookSourceDialog, OnFieldSelect, weld::ComboBox&, rListbox, void)
+ {
+ // the index of the affected list box in our array
+ sal_Int32 nListBoxIndex = rListbox.get_id(0).toInt32();
+ DBG_ASSERT(nListBoxIndex >= 0 && nListBoxIndex < FIELD_CONTROLS_VISIBLE,
+ "AddressBookSourceDialog::OnFieldScroll: invalid list box entry!");
+
+ // update the array where we remember the field selections
+ if (0 == rListbox.get_active())
+ // it's the "no field selection" entry
+ m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex].clear();
+ else
+ // it's a regular field entry
+ m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex] = rListbox.get_active_text();
+ }
+
+
+ void AddressBookSourceDialog::implScrollFields(sal_Int32 _nPos, bool _bAdjustFocus, bool _bAdjustScrollbar)
+ {
+ if (_nPos == m_pImpl->nFieldScrollPos)
+ // nothing to do
+ return;
+
+ // loop through our field control rows and do some adjustments
+ // for the new texts
+ auto pLeftLabelControl = m_pImpl->pFieldLabels.begin();
+ auto pRightLabelControl = pLeftLabelControl+1;
+ auto pLeftColumnLabel = m_pImpl->aFieldLabels.cbegin() + 2 * _nPos;
+ auto pRightColumnLabel = pLeftColumnLabel + 1;
+
+ // for the focus movement and the selection scroll
+ auto pLeftListControl = m_pImpl->pFields.begin();
+ auto pRightListControl = pLeftListControl + 1;
+
+ // for the focus movement
+ sal_Int32 nOldFocusRow = -1;
+ sal_Int32 nOldFocusColumn = 0;
+
+ // for the selection scroll
+ auto pLeftAssignment = m_pImpl->aFieldAssignments.cbegin() + 2 * _nPos;
+ auto pRightAssignment = pLeftAssignment + 1;
+
+ // loop
+ for (sal_Int32 i=0; i<FIELD_PAIRS_VISIBLE; ++i)
+ {
+ if ((*pLeftListControl)->has_focus())
+ {
+ nOldFocusRow = i;
+ nOldFocusColumn = 0;
+ }
+ else if ((*pRightListControl)->has_focus())
+ {
+ nOldFocusRow = i;
+ nOldFocusColumn = 1;
+ }
+
+ // the new texts of the label controls
+ (*pLeftLabelControl)->set_label(*pLeftColumnLabel);
+ (*pRightLabelControl)->set_label(*pRightColumnLabel);
+
+ // we may have to hide the controls in the right column, if we have no label text for it
+ // (which means we have an odd number of fields, though we forced our internal arrays to
+ // be even-sized for easier handling)
+ // (If sometimes we support an arbitrary number of field assignments, we would have to care for
+ // an invisible left hand side column, too. But right now, the left hand side controls are always
+ // visible)
+ bool bHideRightColumn = pRightColumnLabel->isEmpty();
+ (*pRightLabelControl)->set_visible(!bHideRightColumn);
+ (*pRightListControl)->set_visible(!bHideRightColumn);
+ // the new selections of the listboxes
+ implSelectField(pLeftListControl->get(), *pLeftAssignment);
+ implSelectField(pRightListControl->get(), *pRightAssignment);
+
+ // increment ...
+ if ( i < FIELD_PAIRS_VISIBLE - 1 )
+ { // (not in the very last round, here the +=2 could result in an invalid
+ // iterator position, which causes an abort in a non-product version
+ pLeftLabelControl += 2;
+ pRightLabelControl += 2;
+ pLeftColumnLabel += 2;
+ pRightColumnLabel += 2;
+
+ pLeftListControl += 2;
+ pRightListControl += 2;
+ pLeftAssignment += 2;
+ pRightAssignment += 2;
+ }
+ }
+
+ if (_bAdjustFocus && (nOldFocusRow >= 0))
+ { // we have to adjust the focus and one of the list boxes has the focus
+ sal_Int32 nDelta = m_pImpl->nFieldScrollPos - _nPos;
+ // the new row for the focus
+ sal_Int32 nNewFocusRow = nOldFocusRow + nDelta;
+ // normalize
+ nNewFocusRow = std::min(nNewFocusRow, sal_Int32(FIELD_PAIRS_VISIBLE - 1), ::std::less< sal_Int32 >());
+ nNewFocusRow = std::max(nNewFocusRow, sal_Int32(0), ::std::less< sal_Int32 >());
+ // set the new focus (in the same column)
+ m_pImpl->pFields[nNewFocusRow * 2 + nOldFocusColumn]->grab_focus();
+ }
+
+ m_pImpl->nFieldScrollPos = _nPos;
+
+ if (_bAdjustScrollbar)
+ m_xFieldScroller->vadjustment_set_value(m_pImpl->nFieldScrollPos);
+ }
+
+ void AddressBookSourceDialog::implSelectField(weld::ComboBox* pBox, const OUString& rText)
+ {
+ if (!rText.isEmpty())
+ // a valid field name
+ pBox->set_active_text(rText);
+ else
+ // no selection for this item
+ pBox->set_active(0);
+ }
+
+ IMPL_LINK_NOARG(AddressBookSourceDialog, OnDelayedInitialize, void*, void)
+ {
+ // load the initial data from the configuration
+ loadConfiguration();
+ resetTables();
+ // will reset the tables/fields implicitly
+
+ if ( !m_pImpl->bWorkingPersistent )
+ if ( m_pImpl->pFields[0] )
+ m_pImpl->pFields[0]->grab_focus();
+ }
+
+ IMPL_LINK(AddressBookSourceDialog, OnComboSelect, weld::ComboBox&, rBox, void)
+ {
+ if (&rBox == m_xDatasource.get())
+ resetTables();
+ else
+ resetFields();
+ }
+
+ IMPL_STATIC_LINK(AddressBookSourceDialog, OnComboGetFocus, weld::Widget&, rBox, void)
+ {
+ dynamic_cast<weld::ComboBox&>(rBox).save_value();
+ }
+
+ IMPL_LINK(AddressBookSourceDialog, OnComboLoseFocus, weld::Widget&, rControl, void)
+ {
+ weld::ComboBox& rBox = dynamic_cast<weld::ComboBox&>(rControl);
+ if (rBox.get_value_changed_from_saved())
+ {
+ if (&rBox == m_xDatasource.get())
+ resetTables();
+ else
+ resetFields();
+ }
+ }
+
+ IMPL_LINK_NOARG(AddressBookSourceDialog, OnOkClicked, weld::Button&, void)
+ {
+ OUString sSelectedDS = lcl_getSelectedDataSource(*m_xDatasource);
+ if ( m_pImpl->bWorkingPersistent )
+ {
+ m_pImpl->pConfigData->setDatasourceName(sSelectedDS);
+ m_pImpl->pConfigData->setCommand(m_xTable->get_active_text());
+ }
+
+ // AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!
+ assert(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size());
+
+ // set the field assignments
+ auto aAssignment = m_pImpl->aFieldAssignments.cbegin();
+ for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
+ {
+ m_pImpl->pConfigData->setFieldAssignment(logicalFieldName, *aAssignment);
+ ++aAssignment;
+ }
+
+ m_xDialog->response(RET_OK);
+ }
+
+ IMPL_LINK_NOARG(AddressBookSourceDialog, OnAdministrateDatasources, weld::Button&, void)
+ {
+ // create the dialog object
+ Reference< XExecutableDialog > xAdminDialog;
+ try
+ {
+ xAdminDialog = AddressBookSourcePilot::createWithParent(m_xORB, m_xDialog->GetXWindow());
+ }
+ catch(const Exception&) { }
+ if (!xAdminDialog.is())
+ {
+ ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.ui.dialogs.AddressBookSourcePilot", true);
+ return;
+ }
+
+ // execute the dialog
+ try
+ {
+ if ( xAdminDialog->execute() == RET_OK )
+ {
+ Reference<XPropertySet> xProp(xAdminDialog,UNO_QUERY);
+ if ( xProp.is() )
+ {
+ OUString sName;
+ xProp->getPropertyValue("DataSourceName") >>= sName;
+
+ INetURLObject aURL( sName );
+ if( aURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ sName = aFileNotation.get(OFileNotation::N_SYSTEM);
+ }
+ m_xDatasource->append_text(sName);
+ m_pImpl->pConfigData.reset( new AssignmentPersistentData );
+ loadConfiguration();
+ resetTables();
+ // will reset the fields implicitly
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("AddressBookSourceDialog::OnAdministrateDatasources: an error occurred while executing the administration dialog!");
+ }
+
+ // re-fill the data source list
+ // try to preserve the current selection
+
+// initializeDatasources();
+ }
+
+} // namespace svt
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */