1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
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>
56 #include <strings.hxx>
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
;
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
;
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
126 class AssignmentTransientData
: public IAssignmentData
130 OUString m_sTableName
;
131 MapString2String m_aAliases
;
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
;
182 SAL_WARN( "svtools", "AssignmentTransientData::AssignmentTransientData: unknown programmatic name: "
183 << rField
.ProgrammaticName
);
189 OUString
AssignmentTransientData::getDatasourceName() const
195 OUString
AssignmentTransientData::getCommand() const
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
)
212 MapString2String::const_iterator aPos
= m_aAliases
.find( _rLogicalName
);
213 if ( m_aAliases
.end() != aPos
)
214 sReturn
= aPos
->second
;
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
242 class AssignmentPersistentData
243 :public ::utl::ConfigItem
244 ,public IAssignmentData
247 StringBag m_aStoredFields
;
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
);
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
;
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
);
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!");
322 OUString
AssignmentPersistentData::getStringProperty(const OUString
& _rLocalName
) const
325 getProperty( _rLocalName
) >>= 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
);
351 OUString
sDescriptionNodePath(u
"Fields"_ustr
);
354 OUString sFieldElementNodePath
= sDescriptionNodePath
+ "/" + _rLogicalName
;
356 Sequence
< PropertyValue
> aNewFieldDescription
{
357 // Fields/<field>/ProgrammaticFieldName
358 comphelper::makePropertyValue(sFieldElementNodePath
+ "/ProgrammaticFieldName",
360 // Fields/<field>/AssignedFieldName
361 comphelper::makePropertyValue(sFieldElementNodePath
+ "/AssignedFieldName",
365 // just set the new value
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
))
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}}
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}}
448 ,m_xTransientDataSource( _rxTransientDS
)
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
))
467 , m_pImpl( new AddressBookSourceDialogData
)
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
))
478 , m_pImpl( new AddressBookSourceDialogData( _rxTransientDS
, _rDataSourceName
, _rTable
, _rMapping
) )
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
)
497 m_pImpl
->pFieldLabels
[row
* 2 + column
] = m_xBuilder
->weld_label("label" + OUString::number(row
* 2 + column
));
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")
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
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);
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
);
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
)
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
);
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
);
663 AddressBookSourceDialog::~AddressBookSourceDialog()
667 void AddressBookSourceDialog::initializeDatasources()
669 if (!m_xDatabaseContext
.is())
671 DBG_ASSERT(m_xORB
.is(), "AddressBookSourceDialog::initializeDatasources: no service factory!");
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);
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
);
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())
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
;
721 InteractionHandler::createWithParent(m_xORB
, m_xDialog
->GetXWindow()),
724 catch(const Exception
&) { }
727 ShowServiceNotAvailableError(m_xDialog
.get(), u
"com.sun.star.task.InteractionHandler", true);
731 // the currently selected table
732 OUString sOldTable
= m_xTable
->get_active_text();
736 m_xCurrentDatasourceTables
= nullptr;
738 // get the tables of the connection
739 Sequence
< OUString
> aTableNames
;
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
;
755 xDS
.set(m_pImpl
->m_xTransientDataSource
, css::uno::UNO_QUERY
);
758 // build the connection
759 Reference
< XConnection
> xConn
;
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
; }
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
&) { }
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.
803 m_xTable
->set_entry_text(sOldTable
);
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
))
825 m_xCurrentDatasourceTables
->getByName(sSelectedTable
),
826 css::uno::UNO_QUERY
);
827 Reference
< XNameAccess
> xColumns
;
828 if (xSuppTableCols
.is())
829 xColumns
= xSuppTableCols
->getColumns();
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();
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
));
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
);
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
);
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();
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
)
905 // loop through our field control rows and do some adjustments
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;
925 for (sal_Int32 i
=0; i
<FIELD_PAIRS_VISIBLE
; ++i
)
927 if ((*pLeftListControl
)->has_focus())
932 else if ((*pRightListControl
)->has_focus())
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
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
);
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
;
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
);
995 // no selection for this item
999 IMPL_LINK_NOARG(AddressBookSourceDialog
, OnDelayedInitialize
, void*, void)
1001 // load the initial data from the configuration
1002 loadConfiguration();
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())
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())
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
);
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);
1074 // execute the dialog
1077 if ( xAdminDialog
->execute() == RET_OK
)
1079 Reference
<XPropertySet
> xProp(xAdminDialog
,UNO_QUERY
);
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();
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();
1113 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */