use insert function instead of for loop
[LibreOffice.git] / svtools / source / dialogs / addresstemplate.cxx
blob0a211b641e19b94e2f2ddda2ad109d804ab63cd4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <memory>
22 #include <svtools/addresstemplate.hxx>
23 #include <svtools/strings.hrc>
24 #include <svtools/svtresid.hxx>
25 #include <tools/debug.hxx>
26 #include <comphelper/interaction.hxx>
27 #include <comphelper/propertyvalue.hxx>
28 #include <comphelper/string.hxx>
29 #include <unotools/configitem.hxx>
30 #include <utility>
31 #include <vcl/stdtext.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/weld.hxx>
34 #include <sal/log.hxx>
35 #include <comphelper/diagnose_ex.hxx>
36 #include <osl/diagnose.h>
37 #include <com/sun/star/util/AliasProgrammaticPair.hpp>
38 #include <com/sun/star/ui/dialogs/AddressBookSourcePilot.hpp>
39 #include <com/sun/star/beans/PropertyValue.hpp>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/sdb/DatabaseContext.hpp>
42 #include <com/sun/star/sdb/XCompletedConnection.hpp>
43 #include <com/sun/star/sdb/SQLContext.hpp>
44 #include <com/sun/star/sdbc/SQLWarning.hpp>
45 #include <com/sun/star/sdbc/XConnection.hpp>
46 #include <com/sun/star/sdbc/XDataSource.hpp>
47 #include <com/sun/star/task/InteractionHandler.hpp>
48 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
49 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
50 #include <svl/filenotation.hxx>
51 #include <tools/urlobj.hxx>
52 #include <algorithm>
53 #include <map>
54 #include <set>
55 #include <array>
56 #include <strings.hxx>
59 namespace svt
61 using namespace ::com::sun::star::uno;
62 using namespace ::com::sun::star::container;
63 using namespace ::com::sun::star::ui::dialogs;
64 using namespace ::com::sun::star::util;
65 using namespace ::com::sun::star::beans;
66 using namespace ::com::sun::star::sdb;
67 using namespace ::com::sun::star::sdbc;
68 using namespace ::com::sun::star::sdbcx;
69 using namespace ::com::sun::star::task;
70 using namespace ::comphelper;
71 using namespace ::utl;
73 typedef std::set<OUString> StringBag;
74 typedef std::map<OUString, OUString> MapString2String;
76 namespace
78 OUString lcl_getSelectedDataSource( const weld::ComboBox& dataSourceCombo )
80 OUString selectedDataSource = dataSourceCombo.get_active_text();
81 if (dataSourceCombo.find_text(selectedDataSource) == -1)
83 // none of the pre-selected entries -> assume a path to a database document
84 OFileNotation aFileNotation( selectedDataSource, OFileNotation::N_SYSTEM );
85 selectedDataSource = aFileNotation.get( OFileNotation::N_URL );
87 return selectedDataSource;
90 // = IAssignmentData
92 class IAssignmentData
94 public:
95 virtual ~IAssignmentData();
97 /// the data source to use for the address book
98 virtual OUString getDatasourceName() const = 0;
100 /// the command to use for the address book
101 virtual OUString getCommand() const = 0;
103 /// checks whether or not there is an assignment for a given logical field
104 virtual bool hasFieldAssignment(const OUString& _rLogicalName) = 0;
105 /// retrieves the assignment for a given logical field
106 virtual OUString getFieldAssignment(const OUString& _rLogicalName) = 0;
108 /// set the assignment for a given logical field
109 virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) = 0;
111 virtual void setDatasourceName(const OUString& _rName) = 0;
112 virtual void setCommand(const OUString& _rCommand) = 0;
117 IAssignmentData::~IAssignmentData()
122 // = AssignmentTransientData
124 namespace {
126 class AssignmentTransientData : public IAssignmentData
128 protected:
129 OUString m_sDSName;
130 OUString m_sTableName;
131 MapString2String m_aAliases;
133 public:
134 AssignmentTransientData(
135 OUString _aDataSourceName,
136 OUString _aTableName,
137 const Sequence< AliasProgrammaticPair >& _rFields
140 // IAssignmentData overridables
141 virtual OUString getDatasourceName() const override;
142 virtual OUString getCommand() const override;
144 virtual bool hasFieldAssignment(const OUString& _rLogicalName) override;
145 virtual OUString getFieldAssignment(const OUString& _rLogicalName) override;
146 virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) override;
148 virtual void setDatasourceName(const OUString& _rName) override;
149 virtual void setCommand(const OUString& _rCommand) override;
154 AssignmentTransientData::AssignmentTransientData(
155 OUString _aDataSourceName, OUString _aTableName,
156 const Sequence< AliasProgrammaticPair >& _rFields )
157 :m_sDSName(std::move( _aDataSourceName ))
158 ,m_sTableName(std::move( _aTableName ))
160 // fill our aliases structure
161 // first collect all known programmatic names
162 StringBag aKnownNames;
164 OUString const sLogicalFieldNames(STR_LOGICAL_FIELD_NAMES);
165 sal_Int32 nIndex = 0;
168 OUString aToken = sLogicalFieldNames.getToken(0, ';', nIndex);
169 aKnownNames.insert(aToken);
171 while ( nIndex >= 0);
173 // loop through the given names
174 for (const AliasProgrammaticPair& rField : _rFields)
176 if ( aKnownNames.end() != aKnownNames.find( rField.ProgrammaticName ) )
178 m_aAliases[ rField.ProgrammaticName ] = rField.Alias;
180 else
182 SAL_WARN( "svtools", "AssignmentTransientData::AssignmentTransientData: unknown programmatic name: "
183 << rField.ProgrammaticName );
189 OUString AssignmentTransientData::getDatasourceName() const
191 return m_sDSName;
195 OUString AssignmentTransientData::getCommand() const
197 return m_sTableName;
201 bool AssignmentTransientData::hasFieldAssignment(const OUString& _rLogicalName)
203 MapString2String::const_iterator aPos = m_aAliases.find( _rLogicalName );
204 return ( m_aAliases.end() != aPos )
205 && ( !aPos->second.isEmpty() );
209 OUString AssignmentTransientData::getFieldAssignment(const OUString& _rLogicalName)
211 OUString sReturn;
212 MapString2String::const_iterator aPos = m_aAliases.find( _rLogicalName );
213 if ( m_aAliases.end() != aPos )
214 sReturn = aPos->second;
216 return sReturn;
220 void AssignmentTransientData::setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment)
222 m_aAliases[ _rLogicalName ] = _rAssignment;
226 void AssignmentTransientData::setDatasourceName(const OUString&)
228 OSL_FAIL( "AssignmentTransientData::setDatasourceName: cannot be implemented for transient data!" );
232 void AssignmentTransientData::setCommand(const OUString&)
234 OSL_FAIL( "AssignmentTransientData::setCommand: cannot be implemented for transient data!" );
238 // = AssignmentPersistentData
240 namespace {
242 class AssignmentPersistentData
243 :public ::utl::ConfigItem
244 ,public IAssignmentData
246 protected:
247 StringBag m_aStoredFields;
249 protected:
250 css::uno::Any getProperty(const OUString& _rLocalName) const;
252 OUString getStringProperty(const OUString& _rLocalName) const;
254 void setStringProperty(const OUString& _pLocalName, const OUString& _rValue);
256 public:
257 AssignmentPersistentData();
259 // IAssignmentData overridables
260 virtual OUString getDatasourceName() const override;
261 virtual OUString getCommand() const override;
263 virtual bool hasFieldAssignment(const OUString& _rLogicalName) override;
264 virtual OUString getFieldAssignment(const OUString& _rLogicalName) override;
265 virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) override;
267 virtual void setDatasourceName(const OUString& _rName) override;
268 virtual void setCommand(const OUString& _rCommand) override;
270 virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
272 private:
273 virtual void ImplCommit() override;
274 void clearFieldAssignment(const OUString& _rLogicalName);
279 void AssignmentPersistentData::Notify( const css::uno::Sequence<OUString>& )
283 void AssignmentPersistentData::ImplCommit()
288 AssignmentPersistentData::AssignmentPersistentData()
289 :ConfigItem(u"Office.DataAccess/AddressBook"_ustr)
291 const Sequence< OUString > aStoredNames = GetNodeNames(u"Fields"_ustr);
292 m_aStoredFields.insert(aStoredNames.begin(), aStoredNames.end());
295 bool AssignmentPersistentData::hasFieldAssignment(const OUString& _rLogicalName)
297 return (m_aStoredFields.end() != m_aStoredFields.find(_rLogicalName));
301 OUString AssignmentPersistentData::getFieldAssignment(const OUString& _rLogicalName)
303 OUString sAssignment;
304 if (hasFieldAssignment(_rLogicalName))
306 OUString sFieldPath = "Fields/" + _rLogicalName + "/AssignedFieldName";
307 sAssignment = getStringProperty(sFieldPath);
309 return sAssignment;
313 Any AssignmentPersistentData::getProperty(const OUString& _rLocalName) const
315 Sequence< OUString > aProperties(&_rLocalName, 1);
316 Sequence< Any > aValues = const_cast<AssignmentPersistentData*>(this)->GetProperties(aProperties);
317 DBG_ASSERT(aValues.getLength() == 1, "AssignmentPersistentData::getProperty: invalid sequence length!");
318 return aValues[0];
322 OUString AssignmentPersistentData::getStringProperty(const OUString& _rLocalName) const
324 OUString sReturn;
325 getProperty( _rLocalName ) >>= sReturn;
326 return sReturn;
330 void AssignmentPersistentData::setStringProperty(const OUString& _pLocalName, const OUString& _rValue)
332 Sequence< OUString > aNames { _pLocalName };
333 Sequence< Any > aValues{ Any(_rValue) };
334 PutProperties(aNames, aValues);
338 void AssignmentPersistentData::setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment)
340 if (_rAssignment.isEmpty())
342 if (hasFieldAssignment(_rLogicalName))
344 // the assignment exists but it should be reset
345 clearFieldAssignment(_rLogicalName);
347 return;
350 // Fields
351 OUString sDescriptionNodePath(u"Fields"_ustr);
353 // Fields/<field>
354 OUString sFieldElementNodePath = sDescriptionNodePath + "/" + _rLogicalName;
356 Sequence< PropertyValue > aNewFieldDescription{
357 // Fields/<field>/ProgrammaticFieldName
358 comphelper::makePropertyValue(sFieldElementNodePath + "/ProgrammaticFieldName",
359 _rLogicalName),
360 // Fields/<field>/AssignedFieldName
361 comphelper::makePropertyValue(sFieldElementNodePath + "/AssignedFieldName",
362 _rAssignment)
365 // just set the new value
366 bool bSuccess =
367 SetSetProperties(sDescriptionNodePath, aNewFieldDescription);
368 DBG_ASSERT(bSuccess, "AssignmentPersistentData::setFieldAssignment: could not commit the changes a field!");
372 void AssignmentPersistentData::clearFieldAssignment(const OUString& _rLogicalName)
374 if (!hasFieldAssignment(_rLogicalName))
375 // nothing to do
376 return;
378 Sequence< OUString > aNames(&_rLogicalName, 1);
379 ClearNodeElements(u"Fields"_ustr, aNames);
383 OUString AssignmentPersistentData::getDatasourceName() const
385 return getStringProperty( u"DataSourceName"_ustr );
389 OUString AssignmentPersistentData::getCommand() const
391 return getStringProperty( u"Command"_ustr );
395 void AssignmentPersistentData::setDatasourceName(const OUString& _rName)
397 setStringProperty( u"DataSourceName"_ustr, _rName );
401 void AssignmentPersistentData::setCommand(const OUString& _rCommand)
403 setStringProperty( u"Command"_ustr, _rCommand );
407 // = AddressBookSourceDialogData
409 struct AddressBookSourceDialogData
411 std::array<std::unique_ptr<weld::Label>, FIELD_PAIRS_VISIBLE*2> pFieldLabels;
412 std::array<std::unique_ptr<weld::ComboBox>, FIELD_PAIRS_VISIBLE*2> pFields;
414 /// when working transient, we need the data source
415 Reference< XDataSource >
416 m_xTransientDataSource;
417 /// current scroll pos in the field list
418 sal_Int32 nFieldScrollPos;
419 /// indicates that we've an odd field number. This member is for efficiency only, it's redundant.
420 bool bOddFieldNumber : 1;
421 /// indicates that we're working with the real persistent configuration
422 bool bWorkingPersistent : 1;
424 /// the strings to use as labels for the field selection listboxes
425 std::vector<OUString> aFieldLabels;
426 // the current field assignment
427 std::vector<OUString> aFieldAssignments;
428 /// the logical field names
429 std::vector<OUString> aLogicalFieldNames;
431 std::unique_ptr<IAssignmentData> pConfigData;
434 AddressBookSourceDialogData( )
435 :pFieldLabels{{nullptr}}
436 ,pFields{{nullptr}}
437 ,nFieldScrollPos(0)
438 ,bOddFieldNumber(false)
439 ,bWorkingPersistent( true )
440 ,pConfigData( new AssignmentPersistentData )
444 AddressBookSourceDialogData( const Reference< XDataSource >& _rxTransientDS, const OUString& _rDataSourceName,
445 const OUString& _rTableName, const Sequence< AliasProgrammaticPair >& _rFields )
446 :pFieldLabels{{nullptr}}
447 ,pFields{{nullptr}}
448 ,m_xTransientDataSource( _rxTransientDS )
449 ,nFieldScrollPos(0)
450 ,bOddFieldNumber(false)
451 ,bWorkingPersistent( false )
452 ,pConfigData( new AssignmentTransientData( _rDataSourceName, _rTableName, _rFields ) )
456 // Copy assignment is forbidden and not implemented.
457 AddressBookSourceDialogData (const AddressBookSourceDialogData &) = delete;
458 AddressBookSourceDialogData & operator= (const AddressBookSourceDialogData &) = delete;
461 // = AddressBookSourceDialog
462 AddressBookSourceDialog::AddressBookSourceDialog(weld::Window* pParent,
463 const Reference< XComponentContext >& _rxORB )
464 : GenericDialogController(pParent, u"svt/ui/addresstemplatedialog.ui"_ustr, u"AddressTemplateDialog"_ustr)
465 , m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))
466 , m_xORB(_rxORB)
467 , m_pImpl( new AddressBookSourceDialogData )
469 implConstruct();
472 AddressBookSourceDialog::AddressBookSourceDialog(weld::Window* pParent, const Reference< XComponentContext >& _rxORB,
473 const Reference< XDataSource >& _rxTransientDS, const OUString& _rDataSourceName,
474 const OUString& _rTable, const Sequence< AliasProgrammaticPair >& _rMapping )
475 : GenericDialogController(pParent, u"svt/ui/addresstemplatedialog.ui"_ustr, u"AddressTemplateDialog"_ustr)
476 , m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))
477 , m_xORB(_rxORB)
478 , m_pImpl( new AddressBookSourceDialogData( _rxTransientDS, _rDataSourceName, _rTable, _rMapping ) )
480 implConstruct();
483 void AddressBookSourceDialog::implConstruct()
485 m_xOKButton = m_xBuilder->weld_button(u"ok"_ustr);
486 m_xDatasource = m_xBuilder->weld_combo_box(u"datasource"_ustr);
487 m_xAdministrateDatasources = m_xBuilder->weld_button(u"admin"_ustr);
488 m_xTable = m_xBuilder->weld_combo_box(u"datatable"_ustr);
489 m_xFieldScroller = m_xBuilder->weld_scrolled_window(u"scrollwindow"_ustr, true);
490 m_xGrid = m_xBuilder->weld_widget(u"grid"_ustr);
492 for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
494 for (sal_Int32 column=0; column<2; ++column)
496 // the label
497 m_pImpl->pFieldLabels[row * 2 + column] = m_xBuilder->weld_label("label" + OUString::number(row * 2 + column));
498 // the listbox
499 m_pImpl->pFields[row * 2 + column] = m_xBuilder->weld_combo_box("box" + OUString::number(row * 2 + column));
500 m_pImpl->pFields[row * 2 + column]->connect_changed(LINK(this, AddressBookSourceDialog, OnFieldSelect));
504 initializeDatasources();
506 // for the moment, we have a hard coded list of all known fields.
507 // A better solution would be to store all known field translations in the configuration, which could be
508 // extensible by the user in an arbitrary way.
509 // But for the moment we need a quick solution ...
510 // (the main thing would be to store the translations to use here in the user interface, besides that, the code
511 // should be adjustable with a rather small effort.)
513 // initialize the strings for the field labels
514 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_FIRSTNAME ));
515 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_LASTNAME ));
516 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_COMPANY));
517 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_DEPARTMENT ));
518 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_STREET ));
519 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ZIPCODE ));
520 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_CITY ));
521 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_STATE));
522 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_COUNTRY ));
523 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_HOMETEL ));
524 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_WORKTEL ));
525 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_OFFICETEL));
526 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_MOBILE));
527 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_TELOTHER));
528 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_PAGER));
529 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_FAX ));
530 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_EMAIL ));
531 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_URL ));
532 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_TITLE ));
533 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_POSITION ));
534 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_INITIALS ));
535 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ADDRFORM ));
536 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_SALUTATION ));
537 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ID));
538 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_CALENDAR));
539 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_INVITE));
540 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_NOTE));
541 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER1));
542 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER2));
543 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER3));
544 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER4));
546 tools::Long nLabelWidth = 0;
547 tools::Long nListBoxWidth = m_pImpl->pFields[0]->get_approximate_digit_width() * 18;
548 for (auto const& fieldLabel : m_pImpl->aFieldLabels)
550 m_pImpl->pFieldLabels[0]->set_label(fieldLabel);
551 nLabelWidth = std::max(nLabelWidth, m_pImpl->pFieldLabels[0]->get_preferred_size().Width());
553 for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
555 for (sal_Int32 column=0; column<2; ++column)
557 m_pImpl->pFieldLabels[row * 2 + column]->set_size_request(nLabelWidth, -1);
558 m_pImpl->pFields[row * 2 + column]->set_size_request(nListBoxWidth, -1);
562 // force an even number of known fields
563 m_pImpl->bOddFieldNumber = (m_pImpl->aFieldLabels.size() % 2) != 0;
564 if (m_pImpl->bOddFieldNumber)
565 m_pImpl->aFieldLabels.emplace_back();
567 // limit the scrollbar range accordingly
568 sal_Int32 nOverallFieldPairs = m_pImpl->aFieldLabels.size() / 2;
569 m_xFieldScroller->vadjustment_configure(0, 0, nOverallFieldPairs,
570 1, FIELD_PAIRS_VISIBLE - 1, FIELD_PAIRS_VISIBLE);
572 // reset the current field assignments
573 m_pImpl->aFieldAssignments.resize(m_pImpl->aFieldLabels.size());
574 // (empty strings mean "no assignment")
576 // some knittings
577 m_xFieldScroller->connect_vadjustment_changed(LINK(this, AddressBookSourceDialog, OnFieldScroll));
578 m_xAdministrateDatasources->connect_clicked(LINK(this, AddressBookSourceDialog, OnAdministrateDatasources));
579 m_xDatasource->set_entry_completion(true);
580 m_xTable->set_entry_completion(true);
581 m_xTable->connect_focus_in(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
582 m_xDatasource->connect_focus_in(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
583 m_xTable->connect_focus_out(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
584 m_xDatasource->connect_focus_out(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
585 m_xTable->connect_changed(LINK(this, AddressBookSourceDialog, OnComboSelect));
586 m_xDatasource->connect_changed(LINK(this, AddressBookSourceDialog, OnComboSelect));
587 m_xOKButton->connect_clicked(LINK(this, AddressBookSourceDialog, OnOkClicked));
589 // initialize the field controls
590 resetFields();
591 // tdf#136494 wait until contents are filled before getting preferred height
592 m_xFieldScroller->set_size_request(-1, m_xGrid->get_preferred_size().Height());
593 m_xFieldScroller->vadjustment_set_value(0);
594 m_pImpl->nFieldScrollPos = -1;
595 implScrollFields(0, false, false);
597 // the logical names
598 OUString sLogicalFieldNames(STR_LOGICAL_FIELD_NAMES);
599 sal_Int32 nAdjustedTokenCount = comphelper::string::getTokenCount(sLogicalFieldNames, ';') + (m_pImpl->bOddFieldNumber ? 1 : 0);
600 DBG_ASSERT(nAdjustedTokenCount == static_cast<sal_Int32>(m_pImpl->aFieldLabels.size()),
601 "AddressBookSourceDialog::AddressBookSourceDialog: inconsistence between logical and UI field names!");
602 m_pImpl->aLogicalFieldNames.reserve(nAdjustedTokenCount);
603 sal_Int32 nIdx{ 0 };
604 for (sal_Int32 i = 0; i<nAdjustedTokenCount; ++i)
605 m_pImpl->aLogicalFieldNames.push_back(sLogicalFieldNames.getToken(0, ';', nIdx));
607 Application::PostUserEvent(LINK(this, AddressBookSourceDialog, OnDelayedInitialize), nullptr, false);
609 // so the dialog will at least show up before we do the loading of the
610 // configuration data and the (maybe time consuming) analysis of the data source/table to select
612 if (m_pImpl->bWorkingPersistent)
613 return;
615 m_xAdministrateDatasources->hide();
618 void AddressBookSourceDialog::getFieldMapping(Sequence< AliasProgrammaticPair >& _rMapping) const
620 _rMapping.realloc( m_pImpl->aLogicalFieldNames.size() );
621 AliasProgrammaticPair* pPair = _rMapping.getArray();
623 for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
625 if ( m_pImpl->pConfigData->hasFieldAssignment(logicalFieldName) )
627 // the user gave us an assignment for this field
628 pPair->ProgrammaticName = logicalFieldName;
629 pPair->Alias = m_pImpl->pConfigData->getFieldAssignment(logicalFieldName);
630 ++pPair;
634 _rMapping.realloc( pPair - _rMapping.getArray() );
637 void AddressBookSourceDialog::loadConfiguration()
639 OUString sName = m_pImpl->pConfigData->getDatasourceName();
640 INetURLObject aURL( sName );
641 if( aURL.GetProtocol() != INetProtocol::NotValid )
643 OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
644 sName = aFileNotation.get(OFileNotation::N_SYSTEM);
647 m_xDatasource->set_entry_text(sName);
648 m_xTable->set_entry_text(m_pImpl->pConfigData->getCommand());
649 // we ignore the CommandType: only tables are supported
651 // the logical names for the fields
652 // AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!
653 assert(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size());
655 auto aAssignment = m_pImpl->aFieldAssignments.begin();
656 for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
658 *aAssignment = m_pImpl->pConfigData->getFieldAssignment(logicalFieldName);
659 ++aAssignment;
663 AddressBookSourceDialog::~AddressBookSourceDialog()
667 void AddressBookSourceDialog::initializeDatasources()
669 if (!m_xDatabaseContext.is())
671 DBG_ASSERT(m_xORB.is(), "AddressBookSourceDialog::initializeDatasources: no service factory!");
672 if (!m_xORB.is())
673 return;
677 m_xDatabaseContext = DatabaseContext::create(m_xORB);
679 catch(const Exception&) { }
680 if (!m_xDatabaseContext.is())
682 ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.sdb.DatabaseContext", false);
683 return;
686 m_xDatasource->clear();
688 // fill the datasources listbox
691 const css::uno::Sequence<OUString> aElementNames = m_xDatabaseContext->getElementNames();
692 for (const OUString& rDatasourceName : aElementNames)
693 m_xDatasource->append_text(rDatasourceName);
695 catch(Exception&)
697 TOOLS_WARN_EXCEPTION( "svtools", "AddressBookSourceDialog::initializeDatasources: caught an exception while asking for the data source names!");
701 IMPL_LINK(AddressBookSourceDialog, OnFieldScroll, weld::ScrolledWindow&, rScrollBar, void)
703 implScrollFields(rScrollBar.vadjustment_get_value(), true, true);
706 void AddressBookSourceDialog::resetTables()
708 if (!m_xDatabaseContext.is())
709 return;
711 weld::WaitObject aWaitCursor(m_xDialog.get());
713 // no matter what we do here, we handled the currently selected data source (no matter if successful or not)
714 m_xDatasource->save_value();
716 // create an interaction handler (may be needed for connecting)
717 Reference< XInteractionHandler > xHandler;
720 xHandler.set(
721 InteractionHandler::createWithParent(m_xORB, m_xDialog->GetXWindow()),
722 UNO_QUERY_THROW );
724 catch(const Exception&) { }
725 if (!xHandler.is())
727 ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.task.InteractionHandler", true);
728 return;
731 // the currently selected table
732 OUString sOldTable = m_xTable->get_active_text();
734 m_xTable->clear();
736 m_xCurrentDatasourceTables= nullptr;
738 // get the tables of the connection
739 Sequence< OUString > aTableNames;
740 Any aException;
743 Reference< XCompletedConnection > xDS;
744 if ( m_pImpl->bWorkingPersistent )
746 OUString sSelectedDS = lcl_getSelectedDataSource(*m_xDatasource);
748 // get the data source the user has chosen and let it build a connection
749 INetURLObject aURL( sSelectedDS );
750 if ( aURL.GetProtocol() != INetProtocol::NotValid || m_xDatabaseContext->hasByName(sSelectedDS) )
751 m_xDatabaseContext->getByName( sSelectedDS ) >>= xDS;
753 else
755 xDS.set(m_pImpl->m_xTransientDataSource, css::uno::UNO_QUERY);
758 // build the connection
759 Reference< XConnection > xConn;
760 if (xDS.is())
761 xConn = xDS->connectWithCompletion(xHandler);
763 // get the table names
764 Reference< XTablesSupplier > xSupplTables(xConn, UNO_QUERY);
765 if (xSupplTables.is())
767 m_xCurrentDatasourceTables = xSupplTables->getTables();
768 if (m_xCurrentDatasourceTables.is())
769 aTableNames = m_xCurrentDatasourceTables->getElementNames();
772 catch(const SQLContext& e) { aException <<= e; }
773 catch(const SQLWarning& e) { aException <<= e; }
774 catch(const SQLException& e) { aException <<= e; }
775 catch(Exception&)
777 OSL_FAIL("AddressBookSourceDialog::resetTables: could not retrieve the table!");
780 if (aException.hasValue())
782 Reference< XInteractionRequest > xRequest = new OInteractionRequest(aException);
785 xHandler->handle(xRequest);
787 catch(Exception&) { }
788 return;
791 bool bKnowOldTable = false;
792 // fill the table list
793 for (const OUString& rTableName : aTableNames)
795 m_xTable->append_text(rTableName);
796 if (rTableName == sOldTable)
797 bKnowOldTable = true;
800 // set the old table, if the new data source knows a table with this name, too. Else reset the tables edit field.
801 if (!bKnowOldTable)
802 sOldTable.clear();
803 m_xTable->set_entry_text(sOldTable);
805 resetFields();
808 void AddressBookSourceDialog::resetFields()
810 weld::WaitObject aWaitCursor(m_xDialog.get());
812 // no matter what we do here, we handled the currently selected table (no matter if successful or not)
813 m_xDatasource->save_value();
815 OUString sSelectedTable = m_xTable->get_active_text();
816 Sequence< OUString > aColumnNames;
819 if (m_xCurrentDatasourceTables.is())
821 // get the table and the columns
822 Reference< XColumnsSupplier > xSuppTableCols;
823 if (m_xCurrentDatasourceTables->hasByName(sSelectedTable))
824 xSuppTableCols.set(
825 m_xCurrentDatasourceTables->getByName(sSelectedTable),
826 css::uno::UNO_QUERY);
827 Reference< XNameAccess > xColumns;
828 if (xSuppTableCols.is())
829 xColumns = xSuppTableCols->getColumns();
830 if (xColumns.is())
831 aColumnNames = xColumns->getElementNames();
834 catch (const Exception&)
836 OSL_FAIL("AddressBookSourceDialog::resetFields: could not retrieve the table columns!");
840 // for quicker access
841 ::std::set< OUString > aColumnNameSet(std::cbegin(aColumnNames), std::cend(aColumnNames));
843 std::vector<OUString>::iterator aInitialSelection = m_pImpl->aFieldAssignments.begin() + m_pImpl->nFieldScrollPos;
845 OUString sSaveSelection;
846 for (sal_Int32 i=0; i<FIELD_CONTROLS_VISIBLE; ++i, ++aInitialSelection)
848 weld::ComboBox* pListbox = m_pImpl->pFields[i].get();
849 sSaveSelection = pListbox->get_active_text();
851 pListbox->clear();
853 // the one entry for "no selection"
854 pListbox->append_text(m_sNoFieldSelection);
855 // as it's entry data, set the index of the list box in our array
856 pListbox->set_id(0, OUString::number(i));
858 // the field names
859 for (const OUString& rColumnName : aColumnNames)
860 pListbox->append_text(rColumnName);
862 if (!aInitialSelection->isEmpty() && (aColumnNameSet.end() != aColumnNameSet.find(*aInitialSelection)))
863 // we can select the entry as specified in our field assignment array
864 pListbox->set_active_text(*aInitialSelection);
865 else
866 // try to restore the selection
867 if (aColumnNameSet.end() != aColumnNameSet.find(sSaveSelection))
868 // the old selection is a valid column name
869 pListbox->set_active_text(sSaveSelection);
870 else
871 // select the <none> entry
872 pListbox->set_active(0);
875 // adjust m_pImpl->aFieldAssignments
876 for (auto & fieldAssignment : m_pImpl->aFieldAssignments)
877 if (!fieldAssignment.isEmpty())
878 if (aColumnNameSet.end() == aColumnNameSet.find(fieldAssignment))
879 fieldAssignment.clear();
882 IMPL_LINK(AddressBookSourceDialog, OnFieldSelect, weld::ComboBox&, rListbox, void)
884 // the index of the affected list box in our array
885 sal_Int32 nListBoxIndex = rListbox.get_id(0).toInt32();
886 DBG_ASSERT(nListBoxIndex >= 0 && nListBoxIndex < FIELD_CONTROLS_VISIBLE,
887 "AddressBookSourceDialog::OnFieldScroll: invalid list box entry!");
889 // update the array where we remember the field selections
890 if (0 == rListbox.get_active())
891 // it's the "no field selection" entry
892 m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex].clear();
893 else
894 // it's a regular field entry
895 m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex] = rListbox.get_active_text();
899 void AddressBookSourceDialog::implScrollFields(sal_Int32 _nPos, bool _bAdjustFocus, bool _bAdjustScrollbar)
901 if (_nPos == m_pImpl->nFieldScrollPos)
902 // nothing to do
903 return;
905 // loop through our field control rows and do some adjustments
906 // for the new texts
907 auto pLeftLabelControl = m_pImpl->pFieldLabels.begin();
908 auto pRightLabelControl = pLeftLabelControl+1;
909 auto pLeftColumnLabel = m_pImpl->aFieldLabels.cbegin() + 2 * _nPos;
910 auto pRightColumnLabel = pLeftColumnLabel + 1;
912 // for the focus movement and the selection scroll
913 auto pLeftListControl = m_pImpl->pFields.begin();
914 auto pRightListControl = pLeftListControl + 1;
916 // for the focus movement
917 sal_Int32 nOldFocusRow = -1;
918 sal_Int32 nOldFocusColumn = 0;
920 // for the selection scroll
921 auto pLeftAssignment = m_pImpl->aFieldAssignments.cbegin() + 2 * _nPos;
922 auto pRightAssignment = pLeftAssignment + 1;
924 // loop
925 for (sal_Int32 i=0; i<FIELD_PAIRS_VISIBLE; ++i)
927 if ((*pLeftListControl)->has_focus())
929 nOldFocusRow = i;
930 nOldFocusColumn = 0;
932 else if ((*pRightListControl)->has_focus())
934 nOldFocusRow = i;
935 nOldFocusColumn = 1;
938 // the new texts of the label controls
939 (*pLeftLabelControl)->set_label(*pLeftColumnLabel);
940 (*pRightLabelControl)->set_label(*pRightColumnLabel);
942 // we may have to hide the controls in the right column, if we have no label text for it
943 // (which means we have an odd number of fields, though we forced our internal arrays to
944 // be even-sized for easier handling)
945 // (If sometimes we support an arbitrary number of field assignments, we would have to care for
946 // an invisible left hand side column, too. But right now, the left hand side controls are always
947 // visible)
948 bool bHideRightColumn = pRightColumnLabel->isEmpty();
949 (*pRightLabelControl)->set_visible(!bHideRightColumn);
950 (*pRightListControl)->set_visible(!bHideRightColumn);
951 // the new selections of the listboxes
952 implSelectField(pLeftListControl->get(), *pLeftAssignment);
953 implSelectField(pRightListControl->get(), *pRightAssignment);
955 // increment ...
956 if ( i < FIELD_PAIRS_VISIBLE - 1 )
957 { // (not in the very last round, here the +=2 could result in an invalid
958 // iterator position, which causes an abort in a non-product version
959 pLeftLabelControl += 2;
960 pRightLabelControl += 2;
961 pLeftColumnLabel += 2;
962 pRightColumnLabel += 2;
964 pLeftListControl += 2;
965 pRightListControl += 2;
966 pLeftAssignment += 2;
967 pRightAssignment += 2;
971 if (_bAdjustFocus && (nOldFocusRow >= 0))
972 { // we have to adjust the focus and one of the list boxes has the focus
973 sal_Int32 nDelta = m_pImpl->nFieldScrollPos - _nPos;
974 // the new row for the focus
975 sal_Int32 nNewFocusRow = nOldFocusRow + nDelta;
976 // normalize
977 nNewFocusRow = std::min(nNewFocusRow, sal_Int32(FIELD_PAIRS_VISIBLE - 1), ::std::less< sal_Int32 >());
978 nNewFocusRow = std::max(nNewFocusRow, sal_Int32(0), ::std::less< sal_Int32 >());
979 // set the new focus (in the same column)
980 m_pImpl->pFields[nNewFocusRow * 2 + nOldFocusColumn]->grab_focus();
983 m_pImpl->nFieldScrollPos = _nPos;
985 if (_bAdjustScrollbar)
986 m_xFieldScroller->vadjustment_set_value(m_pImpl->nFieldScrollPos);
989 void AddressBookSourceDialog::implSelectField(weld::ComboBox* pBox, const OUString& rText)
991 if (!rText.isEmpty())
992 // a valid field name
993 pBox->set_active_text(rText);
994 else
995 // no selection for this item
996 pBox->set_active(0);
999 IMPL_LINK_NOARG(AddressBookSourceDialog, OnDelayedInitialize, void*, void)
1001 // load the initial data from the configuration
1002 loadConfiguration();
1003 resetTables();
1004 // will reset the tables/fields implicitly
1006 if ( !m_pImpl->bWorkingPersistent )
1007 if ( m_pImpl->pFields[0] )
1008 m_pImpl->pFields[0]->grab_focus();
1011 IMPL_LINK(AddressBookSourceDialog, OnComboSelect, weld::ComboBox&, rBox, void)
1013 if (&rBox == m_xDatasource.get())
1014 resetTables();
1015 else
1016 resetFields();
1019 IMPL_STATIC_LINK(AddressBookSourceDialog, OnComboGetFocus, weld::Widget&, rBox, void)
1021 dynamic_cast<weld::ComboBox&>(rBox).save_value();
1024 IMPL_LINK(AddressBookSourceDialog, OnComboLoseFocus, weld::Widget&, rControl, void)
1026 weld::ComboBox& rBox = dynamic_cast<weld::ComboBox&>(rControl);
1027 if (rBox.get_value_changed_from_saved())
1029 if (&rBox == m_xDatasource.get())
1030 resetTables();
1031 else
1032 resetFields();
1036 IMPL_LINK_NOARG(AddressBookSourceDialog, OnOkClicked, weld::Button&, void)
1038 OUString sSelectedDS = lcl_getSelectedDataSource(*m_xDatasource);
1039 if ( m_pImpl->bWorkingPersistent )
1041 m_pImpl->pConfigData->setDatasourceName(sSelectedDS);
1042 m_pImpl->pConfigData->setCommand(m_xTable->get_active_text());
1045 // AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!
1046 assert(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size());
1048 // set the field assignments
1049 auto aAssignment = m_pImpl->aFieldAssignments.cbegin();
1050 for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
1052 m_pImpl->pConfigData->setFieldAssignment(logicalFieldName, *aAssignment);
1053 ++aAssignment;
1056 m_xDialog->response(RET_OK);
1059 IMPL_LINK_NOARG(AddressBookSourceDialog, OnAdministrateDatasources, weld::Button&, void)
1061 // create the dialog object
1062 Reference< XExecutableDialog > xAdminDialog;
1065 xAdminDialog = AddressBookSourcePilot::createWithParent(m_xORB, m_xDialog->GetXWindow());
1067 catch(const Exception&) { }
1068 if (!xAdminDialog.is())
1070 ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.ui.dialogs.AddressBookSourcePilot", true);
1071 return;
1074 // execute the dialog
1077 if ( xAdminDialog->execute() == RET_OK )
1079 Reference<XPropertySet> xProp(xAdminDialog,UNO_QUERY);
1080 if ( xProp.is() )
1082 OUString sName;
1083 xProp->getPropertyValue(u"DataSourceName"_ustr) >>= sName;
1085 INetURLObject aURL( sName );
1086 if( aURL.GetProtocol() != INetProtocol::NotValid )
1088 OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1089 sName = aFileNotation.get(OFileNotation::N_SYSTEM);
1091 m_xDatasource->append_text(sName);
1092 m_pImpl->pConfigData.reset( new AssignmentPersistentData );
1093 loadConfiguration();
1094 resetTables();
1095 // will reset the fields implicitly
1099 catch(const Exception&)
1101 OSL_FAIL("AddressBookSourceDialog::OnAdministrateDatasources: an error occurred while executing the administration dialog!");
1104 // re-fill the data source list
1105 // try to preserve the current selection
1107 // initializeDatasources();
1110 } // namespace svt
1113 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */