LibreOffice Module svtools (master) 1
addresstemplate.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
21#include <memory>
23#include <svtools/strings.hrc>
24#include <svtools/svtresid.hxx>
25#include <tools/debug.hxx>
28#include <comphelper/string.hxx>
30#include <utility>
31#include <vcl/stdtext.hxx>
32#include <vcl/svapp.hxx>
33#include <vcl/weld.hxx>
34#include <sal/log.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>
57
58
59namespace svt
60{
61 using namespace ::com::sun::star::uno;
62 using namespace ::com::sun::star::lang;
63 using namespace ::com::sun::star::container;
64 using namespace ::com::sun::star::ui::dialogs;
65 using namespace ::com::sun::star::util;
66 using namespace ::com::sun::star::beans;
67 using namespace ::com::sun::star::sdb;
68 using namespace ::com::sun::star::sdbc;
69 using namespace ::com::sun::star::sdbcx;
70 using namespace ::com::sun::star::task;
71 using namespace ::comphelper;
72 using namespace ::utl;
73
74 typedef std::set<OUString> StringBag;
75 typedef std::map<OUString, OUString> MapString2String;
76
77 namespace
78 {
79 OUString lcl_getSelectedDataSource( const weld::ComboBox& dataSourceCombo )
80 {
81 OUString selectedDataSource = dataSourceCombo.get_active_text();
82 if (dataSourceCombo.find_text(selectedDataSource) == -1)
83 {
84 // none of the pre-selected entries -> assume a path to a database document
85 OFileNotation aFileNotation( selectedDataSource, OFileNotation::N_SYSTEM );
86 selectedDataSource = aFileNotation.get( OFileNotation::N_URL );
87 }
88 return selectedDataSource;
89 }
90
91 // = IAssignmentData
92
93 class IAssignmentData
94 {
95 public:
96 virtual ~IAssignmentData();
97
99 virtual OUString getDatasourceName() const = 0;
100
102 virtual OUString getCommand() const = 0;
103
105 virtual bool hasFieldAssignment(const OUString& _rLogicalName) = 0;
107 virtual OUString getFieldAssignment(const OUString& _rLogicalName) = 0;
108
110 virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) = 0;
111
112 virtual void setDatasourceName(const OUString& _rName) = 0;
113 virtual void setCommand(const OUString& _rCommand) = 0;
114 };
115
116 }
117
118 IAssignmentData::~IAssignmentData()
119 {
120 }
121
122
123 // = AssignmentTransientData
124
125 namespace {
126
127 class AssignmentTransientData : public IAssignmentData
128 {
129 protected:
130 OUString m_sDSName;
131 OUString m_sTableName;
133
134 public:
135 AssignmentTransientData(
136 OUString _aDataSourceName,
137 OUString _aTableName,
138 const Sequence< AliasProgrammaticPair >& _rFields
139 );
140
141 // IAssignmentData overridables
142 virtual OUString getDatasourceName() const override;
143 virtual OUString getCommand() const override;
144
145 virtual bool hasFieldAssignment(const OUString& _rLogicalName) override;
146 virtual OUString getFieldAssignment(const OUString& _rLogicalName) override;
147 virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) override;
148
149 virtual void setDatasourceName(const OUString& _rName) override;
150 virtual void setCommand(const OUString& _rCommand) override;
151 };
152
153 }
154
155 AssignmentTransientData::AssignmentTransientData(
156 OUString _aDataSourceName, OUString _aTableName,
157 const Sequence< AliasProgrammaticPair >& _rFields )
158 :m_sDSName(std::move( _aDataSourceName ))
159 ,m_sTableName(std::move( _aTableName ))
160 {
161 // fill our aliases structure
162 // first collect all known programmatic names
163 StringBag aKnownNames;
164
165 OUString const sLogicalFieldNames(STR_LOGICAL_FIELD_NAMES);
166 sal_Int32 nIndex = 0;
167 do
168 {
169 OUString aToken = sLogicalFieldNames.getToken(0, ';', nIndex);
170 aKnownNames.insert(aToken);
171 }
172 while ( nIndex >= 0);
173
174 // loop through the given names
175 for (const AliasProgrammaticPair& rField : _rFields)
176 {
177 if ( aKnownNames.end() != aKnownNames.find( rField.ProgrammaticName ) )
178 {
179 m_aAliases[ rField.ProgrammaticName ] = rField.Alias;
180 }
181 else
182 {
183 SAL_WARN( "svtools", "AssignmentTransientData::AssignmentTransientData: unknown programmatic name: "
184 << rField.ProgrammaticName );
185 }
186 }
187 }
188
189
190 OUString AssignmentTransientData::getDatasourceName() const
191 {
192 return m_sDSName;
193 }
194
195
196 OUString AssignmentTransientData::getCommand() const
197 {
198 return m_sTableName;
199 }
200
201
202 bool AssignmentTransientData::hasFieldAssignment(const OUString& _rLogicalName)
203 {
204 MapString2String::const_iterator aPos = m_aAliases.find( _rLogicalName );
205 return ( m_aAliases.end() != aPos )
206 && ( !aPos->second.isEmpty() );
207 }
208
209
210 OUString AssignmentTransientData::getFieldAssignment(const OUString& _rLogicalName)
211 {
212 OUString sReturn;
213 MapString2String::const_iterator aPos = m_aAliases.find( _rLogicalName );
214 if ( m_aAliases.end() != aPos )
215 sReturn = aPos->second;
216
217 return sReturn;
218 }
219
220
221 void AssignmentTransientData::setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment)
222 {
223 m_aAliases[ _rLogicalName ] = _rAssignment;
224 }
225
226
227 void AssignmentTransientData::setDatasourceName(const OUString&)
228 {
229 OSL_FAIL( "AssignmentTransientData::setDatasourceName: cannot be implemented for transient data!" );
230 }
231
232
233 void AssignmentTransientData::setCommand(const OUString&)
234 {
235 OSL_FAIL( "AssignmentTransientData::setCommand: cannot be implemented for transient data!" );
236 }
237
238
239 // = AssignmentPersistentData
240
241 namespace {
242
243 class AssignmentPersistentData
244 :public ::utl::ConfigItem
245 ,public IAssignmentData
246 {
247 protected:
249
250 protected:
251 css::uno::Any getProperty(const OUString& _rLocalName) const;
252
253 OUString getStringProperty(const char* _pLocalName) const;
254 OUString getStringProperty(const OUString& _rLocalName) const;
255
256 void setStringProperty(const char* _pLocalName, const OUString& _rValue);
257
258 public:
259 AssignmentPersistentData();
260
261 // IAssignmentData overridables
262 virtual OUString getDatasourceName() const override;
263 virtual OUString getCommand() const override;
264
265 virtual bool hasFieldAssignment(const OUString& _rLogicalName) override;
266 virtual OUString getFieldAssignment(const OUString& _rLogicalName) override;
267 virtual void setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment) override;
268
269 virtual void setDatasourceName(const OUString& _rName) override;
270 virtual void setCommand(const OUString& _rCommand) override;
271
272 virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
273
274 private:
275 virtual void ImplCommit() override;
276 void clearFieldAssignment(const OUString& _rLogicalName);
277 };
278
279 }
280
281void AssignmentPersistentData::Notify( const css::uno::Sequence<OUString>& )
282{
283}
284
285void AssignmentPersistentData::ImplCommit()
286{
287}
288
289
290 AssignmentPersistentData::AssignmentPersistentData()
291 :ConfigItem("Office.DataAccess/AddressBook")
292 {
293 const Sequence< OUString > aStoredNames = GetNodeNames("Fields");
294 m_aStoredFields.insert(aStoredNames.begin(), aStoredNames.end());
295 }
296
297 bool AssignmentPersistentData::hasFieldAssignment(const OUString& _rLogicalName)
298 {
299 return (m_aStoredFields.end() != m_aStoredFields.find(_rLogicalName));
300 }
301
302
303 OUString AssignmentPersistentData::getFieldAssignment(const OUString& _rLogicalName)
304 {
305 OUString sAssignment;
306 if (hasFieldAssignment(_rLogicalName))
307 {
308 OUString sFieldPath = "Fields/" + _rLogicalName + "/AssignedFieldName";
309 sAssignment = getStringProperty(sFieldPath);
310 }
311 return sAssignment;
312 }
313
314
315 Any AssignmentPersistentData::getProperty(const OUString& _rLocalName) const
316 {
317 Sequence< OUString > aProperties(&_rLocalName, 1);
318 Sequence< Any > aValues = const_cast<AssignmentPersistentData*>(this)->GetProperties(aProperties);
319 DBG_ASSERT(aValues.getLength() == 1, "AssignmentPersistentData::getProperty: invalid sequence length!");
320 return aValues[0];
321 }
322
323
324 OUString AssignmentPersistentData::getStringProperty(const OUString& _rLocalName) const
325 {
326 OUString sReturn;
327 getProperty( _rLocalName ) >>= sReturn;
328 return sReturn;
329 }
330
331
332 OUString AssignmentPersistentData::getStringProperty(const char* _pLocalName) const
333 {
334 OUString sReturn;
335 getProperty(OUString::createFromAscii(_pLocalName)) >>= sReturn;
336 return sReturn;
337 }
338
339
340 void AssignmentPersistentData::setStringProperty(const char* _pLocalName, const OUString& _rValue)
341 {
342 Sequence< OUString > aNames { OUString::createFromAscii(_pLocalName) };
343 Sequence< Any > aValues{ Any(_rValue) };
344 PutProperties(aNames, aValues);
345 }
346
347
348 void AssignmentPersistentData::setFieldAssignment(const OUString& _rLogicalName, const OUString& _rAssignment)
349 {
350 if (_rAssignment.isEmpty())
351 {
352 if (hasFieldAssignment(_rLogicalName))
353 {
354 // the assignment exists but it should be reset
355 clearFieldAssignment(_rLogicalName);
356 }
357 return;
358 }
359
360 // Fields
361 OUString sDescriptionNodePath("Fields");
362
363 // Fields/<field>
364 OUString sFieldElementNodePath = sDescriptionNodePath + "/" + _rLogicalName;
365
366 Sequence< PropertyValue > aNewFieldDescription{
367 // Fields/<field>/ProgrammaticFieldName
368 comphelper::makePropertyValue(sFieldElementNodePath + "/ProgrammaticFieldName",
369 _rLogicalName),
370 // Fields/<field>/AssignedFieldName
371 comphelper::makePropertyValue(sFieldElementNodePath + "/AssignedFieldName",
372 _rAssignment)
373 };
374
375 // just set the new value
376 bool bSuccess =
377 SetSetProperties(sDescriptionNodePath, aNewFieldDescription);
378 DBG_ASSERT(bSuccess, "AssignmentPersistentData::setFieldAssignment: could not commit the changes a field!");
379 }
380
381
382 void AssignmentPersistentData::clearFieldAssignment(const OUString& _rLogicalName)
383 {
384 if (!hasFieldAssignment(_rLogicalName))
385 // nothing to do
386 return;
387
388 Sequence< OUString > aNames(&_rLogicalName, 1);
389 ClearNodeElements("Fields", aNames);
390 }
391
392
393 OUString AssignmentPersistentData::getDatasourceName() const
394 {
395 return getStringProperty( "DataSourceName" );
396 }
397
398
399 OUString AssignmentPersistentData::getCommand() const
400 {
401 return getStringProperty( "Command" );
402 }
403
404
405 void AssignmentPersistentData::setDatasourceName(const OUString& _rName)
406 {
407 setStringProperty( "DataSourceName", _rName );
408 }
409
410
411 void AssignmentPersistentData::setCommand(const OUString& _rCommand)
412 {
413 setStringProperty( "Command", _rCommand );
414 }
415
416
417 // = AddressBookSourceDialogData
418
420 {
421 std::array<std::unique_ptr<weld::Label>, FIELD_PAIRS_VISIBLE*2> pFieldLabels;
422 std::array<std::unique_ptr<weld::ComboBox>, FIELD_PAIRS_VISIBLE*2> pFields;
423
433
435 std::vector<OUString> aFieldLabels;
436 // the current field assignment
437 std::vector<OUString> aFieldAssignments;
439 std::vector<OUString> aLogicalFieldNames;
440
441 std::unique_ptr<IAssignmentData> pConfigData;
442
443
445 :pFieldLabels{{nullptr}}
446 ,pFields{{nullptr}}
447 ,nFieldScrollPos(0)
448 ,bOddFieldNumber(false)
449 ,bWorkingPersistent( true )
450 ,pConfigData( new AssignmentPersistentData )
451 {
452 }
453
454 AddressBookSourceDialogData( const Reference< XDataSource >& _rxTransientDS, const OUString& _rDataSourceName,
455 const OUString& _rTableName, const Sequence< AliasProgrammaticPair >& _rFields )
456 :pFieldLabels{{nullptr}}
457 ,pFields{{nullptr}}
458 ,m_xTransientDataSource( _rxTransientDS )
459 ,nFieldScrollPos(0)
460 ,bOddFieldNumber(false)
461 ,bWorkingPersistent( false )
462 ,pConfigData( new AssignmentTransientData( _rDataSourceName, _rTableName, _rFields ) )
463 {
464 }
465
466 // Copy assignment is forbidden and not implemented.
469 };
470
471 // = AddressBookSourceDialog
472 AddressBookSourceDialog::AddressBookSourceDialog(weld::Window* pParent,
473 const Reference< XComponentContext >& _rxORB )
474 : GenericDialogController(pParent, "svt/ui/addresstemplatedialog.ui", "AddressTemplateDialog")
475 , m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))
476 , m_xORB(_rxORB)
478 {
479 implConstruct();
480 }
481
482 AddressBookSourceDialog::AddressBookSourceDialog(weld::Window* pParent, const Reference< XComponentContext >& _rxORB,
483 const Reference< XDataSource >& _rxTransientDS, const OUString& _rDataSourceName,
484 const OUString& _rTable, const Sequence< AliasProgrammaticPair >& _rMapping )
485 : GenericDialogController(pParent, "svt/ui/addresstemplatedialog.ui", "AddressTemplateDialog")
486 , m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))
487 , m_xORB(_rxORB)
488 , m_pImpl( new AddressBookSourceDialogData( _rxTransientDS, _rDataSourceName, _rTable, _rMapping ) )
489 {
490 implConstruct();
491 }
492
493 void AddressBookSourceDialog::implConstruct()
494 {
495 m_xOKButton = m_xBuilder->weld_button("ok");
496 m_xDatasource = m_xBuilder->weld_combo_box("datasource");
497 m_xAdministrateDatasources = m_xBuilder->weld_button("admin");
498 m_xTable = m_xBuilder->weld_combo_box("datatable");
499 m_xFieldScroller = m_xBuilder->weld_scrolled_window("scrollwindow", true);
500 m_xGrid = m_xBuilder->weld_widget("grid");
501
502 for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
503 {
504 for (sal_Int32 column=0; column<2; ++column)
505 {
506 // the label
507 m_pImpl->pFieldLabels[row * 2 + column] = m_xBuilder->weld_label("label" + OUString::number(row * 2 + column));
508 // the listbox
509 m_pImpl->pFields[row * 2 + column] = m_xBuilder->weld_combo_box("box" + OUString::number(row * 2 + column));
510 m_pImpl->pFields[row * 2 + column]->connect_changed(LINK(this, AddressBookSourceDialog, OnFieldSelect));
511 }
512 }
513
514 initializeDatasources();
515
516 // for the moment, we have a hard coded list of all known fields.
517 // A better solution would be to store all known field translations in the configuration, which could be
518 // extensible by the user in an arbitrary way.
519 // But for the moment we need a quick solution ...
520 // (the main thing would be to store the translations to use here in the user interface, besides that, the code
521 // should be adjustable with a rather small effort.)
522
523 // initialize the strings for the field labels
524 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_FIRSTNAME ));
525 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_LASTNAME ));
526 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_COMPANY));
527 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_DEPARTMENT ));
528 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_STREET ));
529 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ZIPCODE ));
530 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_CITY ));
531 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_STATE));
532 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_COUNTRY ));
533 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_HOMETEL ));
534 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_WORKTEL ));
535 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_OFFICETEL));
536 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_MOBILE));
537 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_TELOTHER));
538 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_PAGER));
539 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_FAX ));
540 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_EMAIL ));
541 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_URL ));
542 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_TITLE ));
543 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_POSITION ));
544 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_INITIALS ));
545 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ADDRFORM ));
546 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_SALUTATION ));
547 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_ID));
548 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_CALENDAR));
549 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_INVITE));
550 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_NOTE));
551 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER1));
552 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER2));
553 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER3));
554 m_pImpl->aFieldLabels.push_back( SvtResId( STR_FIELD_USER4));
555
556 tools::Long nLabelWidth = 0;
557 tools::Long nListBoxWidth = m_pImpl->pFields[0]->get_approximate_digit_width() * 18;
558 for (auto const& fieldLabel : m_pImpl->aFieldLabels)
559 {
560 m_pImpl->pFieldLabels[0]->set_label(fieldLabel);
561 nLabelWidth = std::max(nLabelWidth, m_pImpl->pFieldLabels[0]->get_preferred_size().Width());
562 }
563 for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
564 {
565 for (sal_Int32 column=0; column<2; ++column)
566 {
567 m_pImpl->pFieldLabels[row * 2 + column]->set_size_request(nLabelWidth, -1);
568 m_pImpl->pFields[row * 2 + column]->set_size_request(nListBoxWidth, -1);
569 }
570 }
571
572 // force an even number of known fields
573 m_pImpl->bOddFieldNumber = (m_pImpl->aFieldLabels.size() % 2) != 0;
574 if (m_pImpl->bOddFieldNumber)
575 m_pImpl->aFieldLabels.emplace_back();
576
577 // limit the scrollbar range accordingly
578 sal_Int32 nOverallFieldPairs = m_pImpl->aFieldLabels.size() / 2;
579 m_xFieldScroller->vadjustment_configure(0, 0, nOverallFieldPairs,
581
582 // reset the current field assignments
583 m_pImpl->aFieldAssignments.resize(m_pImpl->aFieldLabels.size());
584 // (empty strings mean "no assignment")
585
586 // some knittings
587 m_xFieldScroller->connect_vadjustment_changed(LINK(this, AddressBookSourceDialog, OnFieldScroll));
588 m_xAdministrateDatasources->connect_clicked(LINK(this, AddressBookSourceDialog, OnAdministrateDatasources));
589 m_xDatasource->set_entry_completion(true);
590 m_xTable->set_entry_completion(true);
591 m_xTable->connect_focus_in(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
592 m_xDatasource->connect_focus_in(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
593 m_xTable->connect_focus_out(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
594 m_xDatasource->connect_focus_out(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
595 m_xTable->connect_changed(LINK(this, AddressBookSourceDialog, OnComboSelect));
596 m_xDatasource->connect_changed(LINK(this, AddressBookSourceDialog, OnComboSelect));
597 m_xOKButton->connect_clicked(LINK(this, AddressBookSourceDialog, OnOkClicked));
598
599 // initialize the field controls
600 resetFields();
601 // tdf#136494 wait until contents are filled before getting preferred height
602 m_xFieldScroller->set_size_request(-1, m_xGrid->get_preferred_size().Height());
603 m_xFieldScroller->vadjustment_set_value(0);
604 m_pImpl->nFieldScrollPos = -1;
605 implScrollFields(0, false, false);
606
607 // the logical names
608 OUString sLogicalFieldNames(STR_LOGICAL_FIELD_NAMES);
609 sal_Int32 nAdjustedTokenCount = comphelper::string::getTokenCount(sLogicalFieldNames, ';') + (m_pImpl->bOddFieldNumber ? 1 : 0);
610 DBG_ASSERT(nAdjustedTokenCount == static_cast<sal_Int32>(m_pImpl->aFieldLabels.size()),
611 "AddressBookSourceDialog::AddressBookSourceDialog: inconsistence between logical and UI field names!");
612 m_pImpl->aLogicalFieldNames.reserve(nAdjustedTokenCount);
613 sal_Int32 nIdx{ 0 };
614 for (sal_Int32 i = 0; i<nAdjustedTokenCount; ++i)
615 m_pImpl->aLogicalFieldNames.push_back(sLogicalFieldNames.getToken(0, ';', nIdx));
616
617 Application::PostUserEvent(LINK(this, AddressBookSourceDialog, OnDelayedInitialize), nullptr, false);
618
619 // so the dialog will at least show up before we do the loading of the
620 // configuration data and the (maybe time consuming) analysis of the data source/table to select
621
622 if (m_pImpl->bWorkingPersistent)
623 return;
624
625 m_xAdministrateDatasources->hide();
626 }
627
628 void AddressBookSourceDialog::getFieldMapping(Sequence< AliasProgrammaticPair >& _rMapping) const
629 {
630 _rMapping.realloc( m_pImpl->aLogicalFieldNames.size() );
631 AliasProgrammaticPair* pPair = _rMapping.getArray();
632
633 for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
634 {
635 if ( m_pImpl->pConfigData->hasFieldAssignment(logicalFieldName) )
636 {
637 // the user gave us an assignment for this field
638 pPair->ProgrammaticName = logicalFieldName;
639 pPair->Alias = m_pImpl->pConfigData->getFieldAssignment(logicalFieldName);
640 ++pPair;
641 }
642 }
643
644 _rMapping.realloc( pPair - _rMapping.getArray() );
645 }
646
647 void AddressBookSourceDialog::loadConfiguration()
648 {
649 OUString sName = m_pImpl->pConfigData->getDatasourceName();
650 INetURLObject aURL( sName );
651 if( aURL.GetProtocol() != INetProtocol::NotValid )
652 {
653 OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
654 sName = aFileNotation.get(OFileNotation::N_SYSTEM);
655 }
656
657 m_xDatasource->set_entry_text(sName);
658 m_xTable->set_entry_text(m_pImpl->pConfigData->getCommand());
659 // we ignore the CommandType: only tables are supported
660
661 // the logical names for the fields
662 // AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!
663 assert(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size());
664
665 auto aAssignment = m_pImpl->aFieldAssignments.begin();
666 for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
667 {
668 *aAssignment = m_pImpl->pConfigData->getFieldAssignment(logicalFieldName);
669 ++aAssignment;
670 }
671 }
672
673 AddressBookSourceDialog::~AddressBookSourceDialog()
674 {
675 }
676
677 void AddressBookSourceDialog::initializeDatasources()
678 {
679 if (!m_xDatabaseContext.is())
680 {
681 DBG_ASSERT(m_xORB.is(), "AddressBookSourceDialog::initializeDatasources: no service factory!");
682 if (!m_xORB.is())
683 return;
684
685 try
686 {
687 m_xDatabaseContext = DatabaseContext::create(m_xORB);
688 }
689 catch(const Exception&) { }
690 if (!m_xDatabaseContext.is())
691 {
692 ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.sdb.DatabaseContext", false);
693 return;
694 }
695 }
696 m_xDatasource->clear();
697
698 // fill the datasources listbox
699 try
700 {
701 const css::uno::Sequence<OUString> aElementNames = m_xDatabaseContext->getElementNames();
702 for (const OUString& rDatasourceName : aElementNames)
703 m_xDatasource->append_text(rDatasourceName);
704 }
705 catch(Exception&)
706 {
707 TOOLS_WARN_EXCEPTION( "svtools", "AddressBookSourceDialog::initializeDatasources: caught an exception while asking for the data source names!");
708 }
709 }
710
711 IMPL_LINK(AddressBookSourceDialog, OnFieldScroll, weld::ScrolledWindow&, rScrollBar, void)
712 {
713 implScrollFields(rScrollBar.vadjustment_get_value(), true, true);
714 }
715
716 void AddressBookSourceDialog::resetTables()
717 {
718 if (!m_xDatabaseContext.is())
719 return;
720
721 weld::WaitObject aWaitCursor(m_xDialog.get());
722
723 // no matter what we do here, we handled the currently selected data source (no matter if successful or not)
724 m_xDatasource->save_value();
725
726 // create an interaction handler (may be needed for connecting)
727 Reference< XInteractionHandler > xHandler;
728 try
729 {
730 xHandler.set(
731 InteractionHandler::createWithParent(m_xORB, m_xDialog->GetXWindow()),
732 UNO_QUERY_THROW );
733 }
734 catch(const Exception&) { }
735 if (!xHandler.is())
736 {
737 ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.task.InteractionHandler", true);
738 return;
739 }
740
741 // the currently selected table
742 OUString sOldTable = m_xTable->get_active_text();
743
744 m_xTable->clear();
745
746 m_xCurrentDatasourceTables= nullptr;
747
748 // get the tables of the connection
749 Sequence< OUString > aTableNames;
750 Any aException;
751 try
752 {
753 Reference< XCompletedConnection > xDS;
754 if ( m_pImpl->bWorkingPersistent )
755 {
756 OUString sSelectedDS = lcl_getSelectedDataSource(*m_xDatasource);
757
758 // get the data source the user has chosen and let it build a connection
759 INetURLObject aURL( sSelectedDS );
760 if ( aURL.GetProtocol() != INetProtocol::NotValid || m_xDatabaseContext->hasByName(sSelectedDS) )
761 m_xDatabaseContext->getByName( sSelectedDS ) >>= xDS;
762 }
763 else
764 {
765 xDS.set(m_pImpl->m_xTransientDataSource, css::uno::UNO_QUERY);
766 }
767
768 // build the connection
769 Reference< XConnection > xConn;
770 if (xDS.is())
771 xConn = xDS->connectWithCompletion(xHandler);
772
773 // get the table names
774 Reference< XTablesSupplier > xSupplTables(xConn, UNO_QUERY);
775 if (xSupplTables.is())
776 {
777 m_xCurrentDatasourceTables = xSupplTables->getTables();
778 if (m_xCurrentDatasourceTables.is())
779 aTableNames = m_xCurrentDatasourceTables->getElementNames();
780 }
781 }
782 catch(const SQLContext& e) { aException <<= e; }
783 catch(const SQLWarning& e) { aException <<= e; }
784 catch(const SQLException& e) { aException <<= e; }
785 catch(Exception&)
786 {
787 OSL_FAIL("AddressBookSourceDialog::resetTables: could not retrieve the table!");
788 }
789
790 if (aException.hasValue())
791 {
792 Reference< XInteractionRequest > xRequest = new OInteractionRequest(aException);
793 try
794 {
795 xHandler->handle(xRequest);
796 }
797 catch(Exception&) { }
798 return;
799 }
800
801 bool bKnowOldTable = false;
802 // fill the table list
803 for (const OUString& rTableName : std::as_const(aTableNames))
804 {
805 m_xTable->append_text(rTableName);
806 if (rTableName == sOldTable)
807 bKnowOldTable = true;
808 }
809
810 // set the old table, if the new data source knows a table with this name, too. Else reset the tables edit field.
811 if (!bKnowOldTable)
812 sOldTable.clear();
813 m_xTable->set_entry_text(sOldTable);
814
815 resetFields();
816 }
817
818 void AddressBookSourceDialog::resetFields()
819 {
820 weld::WaitObject aWaitCursor(m_xDialog.get());
821
822 // no matter what we do here, we handled the currently selected table (no matter if successful or not)
823 m_xDatasource->save_value();
824
825 OUString sSelectedTable = m_xTable->get_active_text();
826 Sequence< OUString > aColumnNames;
827 try
828 {
829 if (m_xCurrentDatasourceTables.is())
830 {
831 // get the table and the columns
832 Reference< XColumnsSupplier > xSuppTableCols;
833 if (m_xCurrentDatasourceTables->hasByName(sSelectedTable))
834 xSuppTableCols.set(
835 m_xCurrentDatasourceTables->getByName(sSelectedTable),
836 css::uno::UNO_QUERY);
837 Reference< XNameAccess > xColumns;
838 if (xSuppTableCols.is())
839 xColumns = xSuppTableCols->getColumns();
840 if (xColumns.is())
841 aColumnNames = xColumns->getElementNames();
842 }
843 }
844 catch (const Exception&)
845 {
846 OSL_FAIL("AddressBookSourceDialog::resetFields: could not retrieve the table columns!");
847 }
848
849
850 // for quicker access
851 ::std::set< OUString > aColumnNameSet(std::cbegin(aColumnNames), std::cend(aColumnNames));
852
853 std::vector<OUString>::iterator aInitialSelection = m_pImpl->aFieldAssignments.begin() + m_pImpl->nFieldScrollPos;
854
855 OUString sSaveSelection;
856 for (sal_Int32 i=0; i<FIELD_CONTROLS_VISIBLE; ++i, ++aInitialSelection)
857 {
858 weld::ComboBox* pListbox = m_pImpl->pFields[i].get();
859 sSaveSelection = pListbox->get_active_text();
860
861 pListbox->clear();
862
863 // the one entry for "no selection"
864 pListbox->append_text(m_sNoFieldSelection);
865 // as it's entry data, set the index of the list box in our array
866 pListbox->set_id(0, OUString::number(i));
867
868 // the field names
869 for (const OUString& rColumnName : std::as_const(aColumnNames))
870 pListbox->append_text(rColumnName);
871
872 if (!aInitialSelection->isEmpty() && (aColumnNameSet.end() != aColumnNameSet.find(*aInitialSelection)))
873 // we can select the entry as specified in our field assignment array
874 pListbox->set_active_text(*aInitialSelection);
875 else
876 // try to restore the selection
877 if (aColumnNameSet.end() != aColumnNameSet.find(sSaveSelection))
878 // the old selection is a valid column name
879 pListbox->set_active_text(sSaveSelection);
880 else
881 // select the <none> entry
882 pListbox->set_active(0);
883 }
884
885 // adjust m_pImpl->aFieldAssignments
886 for (auto & fieldAssignment : m_pImpl->aFieldAssignments)
887 if (!fieldAssignment.isEmpty())
888 if (aColumnNameSet.end() == aColumnNameSet.find(fieldAssignment))
889 fieldAssignment.clear();
890 }
891
892 IMPL_LINK(AddressBookSourceDialog, OnFieldSelect, weld::ComboBox&, rListbox, void)
893 {
894 // the index of the affected list box in our array
895 sal_Int32 nListBoxIndex = rListbox.get_id(0).toInt32();
896 DBG_ASSERT(nListBoxIndex >= 0 && nListBoxIndex < FIELD_CONTROLS_VISIBLE,
897 "AddressBookSourceDialog::OnFieldScroll: invalid list box entry!");
898
899 // update the array where we remember the field selections
900 if (0 == rListbox.get_active())
901 // it's the "no field selection" entry
902 m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex].clear();
903 else
904 // it's a regular field entry
905 m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex] = rListbox.get_active_text();
906 }
907
908
909 void AddressBookSourceDialog::implScrollFields(sal_Int32 _nPos, bool _bAdjustFocus, bool _bAdjustScrollbar)
910 {
911 if (_nPos == m_pImpl->nFieldScrollPos)
912 // nothing to do
913 return;
914
915 // loop through our field control rows and do some adjustments
916 // for the new texts
917 auto pLeftLabelControl = m_pImpl->pFieldLabels.begin();
918 auto pRightLabelControl = pLeftLabelControl+1;
919 auto pLeftColumnLabel = m_pImpl->aFieldLabels.cbegin() + 2 * _nPos;
920 auto pRightColumnLabel = pLeftColumnLabel + 1;
921
922 // for the focus movement and the selection scroll
923 auto pLeftListControl = m_pImpl->pFields.begin();
924 auto pRightListControl = pLeftListControl + 1;
925
926 // for the focus movement
927 sal_Int32 nOldFocusRow = -1;
928 sal_Int32 nOldFocusColumn = 0;
929
930 // for the selection scroll
931 auto pLeftAssignment = m_pImpl->aFieldAssignments.cbegin() + 2 * _nPos;
932 auto pRightAssignment = pLeftAssignment + 1;
933
934 // loop
935 for (sal_Int32 i=0; i<FIELD_PAIRS_VISIBLE; ++i)
936 {
937 if ((*pLeftListControl)->has_focus())
938 {
939 nOldFocusRow = i;
940 nOldFocusColumn = 0;
941 }
942 else if ((*pRightListControl)->has_focus())
943 {
944 nOldFocusRow = i;
945 nOldFocusColumn = 1;
946 }
947
948 // the new texts of the label controls
949 (*pLeftLabelControl)->set_label(*pLeftColumnLabel);
950 (*pRightLabelControl)->set_label(*pRightColumnLabel);
951
952 // we may have to hide the controls in the right column, if we have no label text for it
953 // (which means we have an odd number of fields, though we forced our internal arrays to
954 // be even-sized for easier handling)
955 // (If sometimes we support an arbitrary number of field assignments, we would have to care for
956 // an invisible left hand side column, too. But right now, the left hand side controls are always
957 // visible)
958 bool bHideRightColumn = pRightColumnLabel->isEmpty();
959 (*pRightLabelControl)->set_visible(!bHideRightColumn);
960 (*pRightListControl)->set_visible(!bHideRightColumn);
961 // the new selections of the listboxes
962 implSelectField(pLeftListControl->get(), *pLeftAssignment);
963 implSelectField(pRightListControl->get(), *pRightAssignment);
964
965 // increment ...
966 if ( i < FIELD_PAIRS_VISIBLE - 1 )
967 { // (not in the very last round, here the +=2 could result in an invalid
968 // iterator position, which causes an abort in a non-product version
969 pLeftLabelControl += 2;
970 pRightLabelControl += 2;
971 pLeftColumnLabel += 2;
972 pRightColumnLabel += 2;
973
974 pLeftListControl += 2;
975 pRightListControl += 2;
976 pLeftAssignment += 2;
977 pRightAssignment += 2;
978 }
979 }
980
981 if (_bAdjustFocus && (nOldFocusRow >= 0))
982 { // we have to adjust the focus and one of the list boxes has the focus
983 sal_Int32 nDelta = m_pImpl->nFieldScrollPos - _nPos;
984 // the new row for the focus
985 sal_Int32 nNewFocusRow = nOldFocusRow + nDelta;
986 // normalize
987 nNewFocusRow = std::min(nNewFocusRow, sal_Int32(FIELD_PAIRS_VISIBLE - 1), ::std::less< sal_Int32 >());
988 nNewFocusRow = std::max(nNewFocusRow, sal_Int32(0), ::std::less< sal_Int32 >());
989 // set the new focus (in the same column)
990 m_pImpl->pFields[nNewFocusRow * 2 + nOldFocusColumn]->grab_focus();
991 }
992
993 m_pImpl->nFieldScrollPos = _nPos;
994
995 if (_bAdjustScrollbar)
996 m_xFieldScroller->vadjustment_set_value(m_pImpl->nFieldScrollPos);
997 }
998
999 void AddressBookSourceDialog::implSelectField(weld::ComboBox* pBox, const OUString& rText)
1000 {
1001 if (!rText.isEmpty())
1002 // a valid field name
1003 pBox->set_active_text(rText);
1004 else
1005 // no selection for this item
1006 pBox->set_active(0);
1007 }
1008
1009 IMPL_LINK_NOARG(AddressBookSourceDialog, OnDelayedInitialize, void*, void)
1010 {
1011 // load the initial data from the configuration
1012 loadConfiguration();
1013 resetTables();
1014 // will reset the tables/fields implicitly
1015
1016 if ( !m_pImpl->bWorkingPersistent )
1017 if ( m_pImpl->pFields[0] )
1018 m_pImpl->pFields[0]->grab_focus();
1019 }
1020
1021 IMPL_LINK(AddressBookSourceDialog, OnComboSelect, weld::ComboBox&, rBox, void)
1022 {
1023 if (&rBox == m_xDatasource.get())
1024 resetTables();
1025 else
1026 resetFields();
1027 }
1028
1029 IMPL_STATIC_LINK(AddressBookSourceDialog, OnComboGetFocus, weld::Widget&, rBox, void)
1030 {
1031 dynamic_cast<weld::ComboBox&>(rBox).save_value();
1032 }
1033
1034 IMPL_LINK(AddressBookSourceDialog, OnComboLoseFocus, weld::Widget&, rControl, void)
1035 {
1036 weld::ComboBox& rBox = dynamic_cast<weld::ComboBox&>(rControl);
1038 {
1039 if (&rBox == m_xDatasource.get())
1040 resetTables();
1041 else
1042 resetFields();
1043 }
1044 }
1045
1046 IMPL_LINK_NOARG(AddressBookSourceDialog, OnOkClicked, weld::Button&, void)
1047 {
1048 OUString sSelectedDS = lcl_getSelectedDataSource(*m_xDatasource);
1049 if ( m_pImpl->bWorkingPersistent )
1050 {
1051 m_pImpl->pConfigData->setDatasourceName(sSelectedDS);
1052 m_pImpl->pConfigData->setCommand(m_xTable->get_active_text());
1053 }
1054
1055 // AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!
1056 assert(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size());
1057
1058 // set the field assignments
1059 auto aAssignment = m_pImpl->aFieldAssignments.cbegin();
1060 for (auto const& logicalFieldName : m_pImpl->aLogicalFieldNames)
1061 {
1062 m_pImpl->pConfigData->setFieldAssignment(logicalFieldName, *aAssignment);
1063 ++aAssignment;
1064 }
1065
1066 m_xDialog->response(RET_OK);
1067 }
1068
1069 IMPL_LINK_NOARG(AddressBookSourceDialog, OnAdministrateDatasources, weld::Button&, void)
1070 {
1071 // create the dialog object
1072 Reference< XExecutableDialog > xAdminDialog;
1073 try
1074 {
1075 xAdminDialog = AddressBookSourcePilot::createWithParent(m_xORB, m_xDialog->GetXWindow());
1076 }
1077 catch(const Exception&) { }
1078 if (!xAdminDialog.is())
1079 {
1080 ShowServiceNotAvailableError(m_xDialog.get(), u"com.sun.star.ui.dialogs.AddressBookSourcePilot", true);
1081 return;
1082 }
1083
1084 // execute the dialog
1085 try
1086 {
1087 if ( xAdminDialog->execute() == RET_OK )
1088 {
1089 Reference<XPropertySet> xProp(xAdminDialog,UNO_QUERY);
1090 if ( xProp.is() )
1091 {
1092 OUString sName;
1093 xProp->getPropertyValue("DataSourceName") >>= sName;
1094
1096 if( aURL.GetProtocol() != INetProtocol::NotValid )
1097 {
1098 OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1099 sName = aFileNotation.get(OFileNotation::N_SYSTEM);
1100 }
1101 m_xDatasource->append_text(sName);
1102 m_pImpl->pConfigData.reset( new AssignmentPersistentData );
1103 loadConfiguration();
1104 resetTables();
1105 // will reset the fields implicitly
1106 }
1107 }
1108 }
1109 catch(const Exception&)
1110 {
1111 OSL_FAIL("AddressBookSourceDialog::OnAdministrateDatasources: an error occurred while executing the administration dialog!");
1112 }
1113
1114 // re-fill the data source list
1115 // try to preserve the current selection
1116
1117// initializeDatasources();
1118 }
1119
1120} // namespace svt
1121
1122
1123/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
PropertiesInfo aProperties
OUString m_sDSName
OUString m_sTableName
StringBag m_aStoredFields
MapString2String m_aAliases
#define FIELD_CONTROLS_VISIBLE
#define FIELD_PAIRS_VISIBLE
Reference< XExecutableDialog > m_xDialog
static ImplSVEvent * PostUserEvent(const Link< void *, void > &rLink, void *pCaller=nullptr, bool bReferenceLink=false)
OUString get(NOTATION _eOutputNotation) const
virtual void Notify(const css::uno::Sequence< OUString > &aPropertyNames)=0
virtual void ImplCommit()=0
bool get_value_changed_from_saved() const
virtual int find_text(const OUString &rStr) const=0
virtual OUString get_active_text() const=0
virtual void clear()=0
virtual void set_id(int row, const OUString &rId)=0
virtual void set_active(int pos)=0
void append_text(const OUString &rStr)
void set_active_text(const OUString &rStr)
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
URL aURL
float u
OUString sName
std::unique_ptr< weld::Button > m_xOKButton
sal_Int32 nIndex
#define SAL_WARN(area, stream)
std::set< OUString > StringBag
std::map< OUString, OUString > MapString2String
@ Exception
sal_Int32 getTokenCount(std::string_view rIn, char cTok)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
IMPL_LINK(AsyncPickerAction, OnActionDone, void *, pEmptyArg, void)
std::set< OUString > StringBag
IMPL_LINK_NOARG(OCommonPicker, OnCancelPicker, void *, void)
std::map< OUString, OUString > MapString2String
IMPL_STATIC_LINK(AddressBookSourceDialog, OnComboGetFocus, weld::Widget &, rBox, void)
SVX_DLLPUBLIC OUString getProperty(css::uno::Reference< css::beans::XPropertyContainer > const &rxPropertyContainer, OUString const &rName)
long Long
void VCL_DLLPUBLIC ShowServiceNotAvailableError(weld::Widget *pParent, std::u16string_view rServiceName, bool bError)
constexpr OUStringLiteral STR_LOGICAL_FIELD_NAMES
Definition: strings.hxx:17
AddressBookSourceDialogData(const AddressBookSourceDialogData &)=delete
Reference< XDataSource > m_xTransientDataSource
when working transient, we need the data source
bool bWorkingPersistent
indicates that we're working with the real persistent configuration
std::vector< OUString > aFieldAssignments
std::unique_ptr< IAssignmentData > pConfigData
bool bOddFieldNumber
indicates that we've an odd field number. This member is for efficiency only, it's redundant.
std::vector< OUString > aFieldLabels
the strings to use as labels for the field selection listboxes
std::array< std::unique_ptr< weld::ComboBox >, FIELD_PAIRS_VISIBLE *2 > pFields
sal_Int32 nFieldScrollPos
current scroll pos in the field list
std::array< std::unique_ptr< weld::Label >, FIELD_PAIRS_VISIBLE *2 > pFieldLabels
AddressBookSourceDialogData(const Reference< XDataSource > &_rxTransientDS, const OUString &_rDataSourceName, const OUString &_rTableName, const Sequence< AliasProgrammaticPair > &_rFields)
std::vector< OUString > aLogicalFieldNames
the logical field names
OUString SvtResId(TranslateId aId)
Definition: svtresid.cxx:24
RET_OK
sal_Int32 _nPos