1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svtools.hxx"
26 
27 #include <stdio.h>
28 #include <svtools/addresstemplate.hxx>
29 #include "addresstemplate.hrc"
30 #include <svtools/svtools.hrc>
31 #include <svtools/helpid.hrc>
32 #include <svtools/svtdata.hxx>
33 #include <tools/debug.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/stl_types.hxx>
36 #include <vcl/stdtext.hxx>
37 #include <vcl/waitobj.hxx>
38 #include <vcl/msgbox.hxx>
39 #include <toolkit/helper/vclunohelper.hxx>
40 #include <comphelper/extract.hxx>
41 #include <comphelper/interaction.hxx>
42 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
43 #include <com/sun/star/awt/XWindow.hpp>
44 #include <com/sun/star/beans/PropertyValue.hpp>
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <com/sun/star/sdb/XCompletedConnection.hpp>
47 #include <com/sun/star/sdb/SQLContext.hpp>
48 #include <com/sun/star/sdbc/SQLWarning.hpp>
49 #include <com/sun/star/sdbc/XConnection.hpp>
50 #include <com/sun/star/task/XInteractionHandler.hpp>
51 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
52 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
53 #include <com/sun/star/sdb/CommandType.hpp>
54 #include <svtools/localresaccess.hxx>
55 #include "svl/filenotation.hxx"
56 #include <tools/urlobj.hxx>
57 #include <algorithm>
58 
59 // .......................................................................
60 namespace svt
61 {
62 // .......................................................................
63 
64 	using namespace ::com::sun::star::uno;
65 	using namespace ::com::sun::star::lang;
66 	using namespace ::com::sun::star::container;
67 	using namespace ::com::sun::star::ui::dialogs;
68 	using namespace ::com::sun::star::util;
69 	using namespace ::com::sun::star::beans;
70 	using namespace ::com::sun::star::sdb;
71 	using namespace ::com::sun::star::sdbc;
72 	using namespace ::com::sun::star::sdbcx;
73 	using namespace ::com::sun::star::task;
74 	using namespace ::comphelper;
75 	using namespace ::utl;
76 
77 	DECLARE_STL_VECTOR( String, StringArray );
78 	DECLARE_STL_STDKEY_SET( ::rtl::OUString, StringBag );
79 	DECLARE_STL_USTRINGACCESS_MAP( ::rtl::OUString, MapString2String );
80 
81     namespace
82     {
83         String lcl_getSelectedDataSource( const ComboBox& _dataSourceCombo )
84         {
85             String selectedDataSource = _dataSourceCombo.GetText();
86             if ( _dataSourceCombo.GetEntryPos( selectedDataSource ) == LISTBOX_ENTRY_NOTFOUND )
87             {
88                 // none of the pre-selected entries -> assume a path to a database document
89 	            OFileNotation aFileNotation( selectedDataSource, OFileNotation::N_SYSTEM );
90 	            selectedDataSource = aFileNotation.get( OFileNotation::N_URL );
91             }
92             return selectedDataSource;
93         }
94     }
95 
96     // ===================================================================
97 	// = IAssigmentData
98 	// ===================================================================
99 	class IAssigmentData
100 	{
101 	public:
102 		virtual ~IAssigmentData();
103 
104 		/// the data source to use for the address book
105 		virtual ::rtl::OUString getDatasourceName() const = 0;
106 
107 		/// the command to use for the address book
108 		virtual ::rtl::OUString getCommand() const = 0;
109 
110 		/** the command type to use for the address book
111 			@return
112 				a <type scope="com.sun.star.sdb">CommandType</type> value
113 		*/
114 		virtual sal_Int32		getCommandType() const = 0;
115 
116 		/// checks whether or not there is an assignment for a given logical field
117 		virtual sal_Bool		hasFieldAssignment(const ::rtl::OUString& _rLogicalName) = 0;
118 		/// retrieves the assignment for a given logical field
119 		virtual ::rtl::OUString getFieldAssignment(const ::rtl::OUString& _rLogicalName) = 0;
120 
121 		/// set the assignment for a given logical field
122 		virtual void			setFieldAssignment(const ::rtl::OUString& _rLogicalName, const ::rtl::OUString& _rAssignment) = 0;
123 		/// clear the assignment for a given logical field
124 		virtual void			clearFieldAssignment(const ::rtl::OUString& _rLogicalName) = 0;
125 
126 		virtual void	setDatasourceName(const ::rtl::OUString& _rName) = 0;
127 		virtual void	setCommand(const ::rtl::OUString& _rCommand) = 0;
128 	};
129 
130 	// -------------------------------------------------------------------
131 	IAssigmentData::~IAssigmentData()
132 	{
133 	}
134 
135 	// ===================================================================
136 	// = AssigmentTransientData
137 	// ===================================================================
138 	class AssigmentTransientData : public IAssigmentData
139 	{
140 	protected:
141         Reference< XDataSource >    m_xDataSource;
142 		::rtl::OUString			    m_sDSName;
143 		::rtl::OUString			    m_sTableName;
144 		MapString2String		    m_aAliases;
145 
146 public:
147 		AssigmentTransientData(
148 			const Reference< XDataSource >& _rxDataSource,
149 			const ::rtl::OUString& _rDataSourceName,
150 			const ::rtl::OUString& _rTableName,
151 			const Sequence< AliasProgrammaticPair >& _rFields
152 		);
153 
154 		// IAssigmentData overridables
155 		virtual ::rtl::OUString getDatasourceName() const;
156 		virtual ::rtl::OUString getCommand() const;
157 		virtual sal_Int32		getCommandType() const;
158 
159 		virtual sal_Bool		hasFieldAssignment(const ::rtl::OUString& _rLogicalName);
160 		virtual ::rtl::OUString getFieldAssignment(const ::rtl::OUString& _rLogicalName);
161 		virtual void			setFieldAssignment(const ::rtl::OUString& _rLogicalName, const ::rtl::OUString& _rAssignment);
162 		virtual void			clearFieldAssignment(const ::rtl::OUString& _rLogicalName);
163 
164 		virtual void	setDatasourceName(const ::rtl::OUString& _rName);
165 		virtual void	setCommand(const ::rtl::OUString& _rCommand);
166 	};
167 
168 	// -------------------------------------------------------------------
169 	AssigmentTransientData::AssigmentTransientData( const Reference< XDataSource >& _rxDataSource,
170 			const ::rtl::OUString& _rDataSourceName, const ::rtl::OUString& _rTableName,
171             const Sequence< AliasProgrammaticPair >& _rFields )
172 		:m_xDataSource( _rxDataSource )
173         ,m_sDSName( _rDataSourceName )
174         ,m_sTableName( _rTableName )
175 	{
176         // fill our aliaes structure
177 		// first collect all known programmatic names
178 		StringBag aKnownNames;
179 
180 		String sLogicalFieldNames( SvtResId( STR_LOCAGICAL_FIELD_NAMES ) );
181 		sal_Int32 nTokenCount = sLogicalFieldNames.GetTokenCount(';');
182 		for (sal_Int32 i = 0; i<nTokenCount; ++i)
183 			aKnownNames.insert(sLogicalFieldNames.GetToken((sal_uInt16)i, ';'));
184 
185 		// loop throuzh the given names
186 		const AliasProgrammaticPair* pFields = _rFields.getConstArray();
187 		const AliasProgrammaticPair* pEnd = pFields + _rFields.getLength();
188 		for (;pFields != pEnd; ++pFields)
189 		{
190 			StringBagIterator aKnownPos = aKnownNames.find( pFields->ProgrammaticName );
191 			if ( aKnownNames.end() != aKnownPos )
192 			{
193 				m_aAliases[ pFields->ProgrammaticName ] = pFields->Alias;
194 			}
195 			else
196 			{
197 				DBG_ERROR	(	(	::rtl::OString("AssigmentTransientData::AssigmentTransientData: unknown programmatic name (")
198 								+=	::rtl::OString(pFields->ProgrammaticName.getStr(), pFields->ProgrammaticName.getLength(), RTL_TEXTENCODING_ASCII_US)
199 								+=	::rtl::OString(")!")
200 								).getStr()
201 							);
202 			}
203 		}
204 	}
205 
206 	// -------------------------------------------------------------------
207 	::rtl::OUString AssigmentTransientData::getDatasourceName() const
208 	{
209 		return m_sDSName;
210 	}
211 
212 	// -------------------------------------------------------------------
213 	::rtl::OUString AssigmentTransientData::getCommand() const
214 	{
215 		return m_sTableName;
216 	}
217 
218 	// -------------------------------------------------------------------
219 	sal_Int32 AssigmentTransientData::getCommandType() const
220 	{
221 		return CommandType::TABLE;
222 	}
223 
224 	// -------------------------------------------------------------------
225 	sal_Bool AssigmentTransientData::hasFieldAssignment(const ::rtl::OUString& _rLogicalName)
226 	{
227 		ConstMapString2StringIterator aPos = m_aAliases.find( _rLogicalName );
228 		return	( m_aAliases.end() != aPos )
229 			&&	( aPos->second.getLength() );
230 	}
231 
232 	// -------------------------------------------------------------------
233 	::rtl::OUString AssigmentTransientData::getFieldAssignment(const ::rtl::OUString& _rLogicalName)
234 	{
235 		::rtl::OUString sReturn;
236 		ConstMapString2StringIterator aPos = m_aAliases.find( _rLogicalName );
237 		if ( m_aAliases.end() != aPos )
238 			sReturn = aPos->second;
239 
240 		return sReturn;
241 	}
242 
243 	// -------------------------------------------------------------------
244 	void AssigmentTransientData::setFieldAssignment(const ::rtl::OUString& _rLogicalName, const ::rtl::OUString& _rAssignment)
245 	{
246 		m_aAliases[ _rLogicalName ] = _rAssignment;
247 	}
248 
249 	// -------------------------------------------------------------------
250 	void AssigmentTransientData::clearFieldAssignment(const ::rtl::OUString& _rLogicalName)
251 	{
252 		MapString2StringIterator aPos = m_aAliases.find( _rLogicalName );
253 		if ( m_aAliases.end() != aPos )
254 			m_aAliases.erase( aPos );
255 	}
256 
257 	// -------------------------------------------------------------------
258 	void AssigmentTransientData::setDatasourceName(const ::rtl::OUString&)
259 	{
260         DBG_ERROR( "AssigmentTransientData::setDatasourceName: cannot be implemented for transient data!" );
261 	}
262 
263 	// -------------------------------------------------------------------
264 	void AssigmentTransientData::setCommand(const ::rtl::OUString&)
265 	{
266         DBG_ERROR( "AssigmentTransientData::setCommand: cannot be implemented for transient data!" );
267 	}
268 
269 	// ===================================================================
270 	// = AssignmentPersistentData
271 	// ===================================================================
272 	class AssignmentPersistentData
273 			:public ::utl::ConfigItem
274 			,public IAssigmentData
275 	{
276 	protected:
277 		StringBag		m_aStoredFields;
278 
279 	protected:
280 		::com::sun::star::uno::Any
281 						getProperty(const ::rtl::OUString& _rLocalName) const;
282 		::com::sun::star::uno::Any
283 						getProperty(const sal_Char* _pLocalName) const;
284 
285 		::rtl::OUString	getStringProperty(const sal_Char* _pLocalName) const;
286 		sal_Int32		getInt32Property(const sal_Char* _pLocalName) const;
287 
288 		::rtl::OUString	getStringProperty(const ::rtl::OUString& _rLocalName) const;
289 
290 		void			setStringProperty(const sal_Char* _pLocalName, const ::rtl::OUString& _rValue);
291 
292 	public:
293 		AssignmentPersistentData();
294 		~AssignmentPersistentData();
295 
296 		// IAssigmentData overridables
297 		virtual ::rtl::OUString getDatasourceName() const;
298 		virtual ::rtl::OUString getCommand() const;
299 		virtual sal_Int32		getCommandType() const;
300 
301 		virtual sal_Bool		hasFieldAssignment(const ::rtl::OUString& _rLogicalName);
302 		virtual ::rtl::OUString getFieldAssignment(const ::rtl::OUString& _rLogicalName);
303 		virtual void			setFieldAssignment(const ::rtl::OUString& _rLogicalName, const ::rtl::OUString& _rAssignment);
304 		virtual void			clearFieldAssignment(const ::rtl::OUString& _rLogicalName);
305 
306 		virtual void	setDatasourceName(const ::rtl::OUString& _rName);
307 		virtual void	setCommand(const ::rtl::OUString& _rCommand);
308 
309         virtual void    Notify( const com::sun::star::uno::Sequence<rtl::OUString>& aPropertyNames);
310 		virtual void	Commit();
311 	};
312 
313 
314 void AssignmentPersistentData::Notify( const com::sun::star::uno::Sequence<rtl::OUString>& )
315 {
316 }
317 
318 void AssignmentPersistentData::Commit()
319 {
320 }
321 
322 	// -------------------------------------------------------------------
323 	AssignmentPersistentData::AssignmentPersistentData()
324 		:ConfigItem( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Office.DataAccess/AddressBook" )))
325 	{
326 		Sequence< ::rtl::OUString > aStoredNames = GetNodeNames(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Fields")));
327 		const ::rtl::OUString* pStoredNames = aStoredNames.getConstArray();
328 		for (sal_Int32 i=0; i<aStoredNames.getLength(); ++i, ++pStoredNames)
329 			m_aStoredFields.insert(*pStoredNames);
330 	}
331 
332 	// -------------------------------------------------------------------
333 	AssignmentPersistentData::~AssignmentPersistentData()
334 	{
335 	}
336 
337 	// -------------------------------------------------------------------
338 	sal_Bool AssignmentPersistentData::hasFieldAssignment(const ::rtl::OUString& _rLogicalName)
339 	{
340 		return (m_aStoredFields.end() != m_aStoredFields.find(_rLogicalName));
341 	}
342 
343 	// -------------------------------------------------------------------
344 	::rtl::OUString AssignmentPersistentData::getFieldAssignment(const ::rtl::OUString& _rLogicalName)
345 	{
346 		::rtl::OUString sAssignment;
347 		if (hasFieldAssignment(_rLogicalName))
348 		{
349 			::rtl::OUString sFieldPath(RTL_CONSTASCII_USTRINGPARAM("Fields/"));
350 			sFieldPath += _rLogicalName;
351 			sFieldPath += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/AssignedFieldName"));
352 			sAssignment = getStringProperty(sFieldPath);
353 		}
354 		return sAssignment;
355 	}
356 
357 	// -------------------------------------------------------------------
358 	Any AssignmentPersistentData::getProperty(const sal_Char* _pLocalName) const
359 	{
360 		return getProperty(::rtl::OUString::createFromAscii(_pLocalName));
361 	}
362 
363 	// -------------------------------------------------------------------
364 	Any AssignmentPersistentData::getProperty(const ::rtl::OUString& _rLocalName) const
365 	{
366 		Sequence< ::rtl::OUString > aProperties(&_rLocalName, 1);
367 		Sequence< Any > aValues = const_cast<AssignmentPersistentData*>(this)->GetProperties(aProperties);
368 		DBG_ASSERT(aValues.getLength() == 1, "AssignmentPersistentData::getProperty: invalid sequence length!");
369 		return aValues[0];
370 	}
371 
372 	// -------------------------------------------------------------------
373 	::rtl::OUString	AssignmentPersistentData::getStringProperty(const ::rtl::OUString& _rLocalName) const
374 	{
375 		::rtl::OUString sReturn;
376 		getProperty( _rLocalName ) >>= sReturn;
377 		return sReturn;
378 	}
379 
380 	// -------------------------------------------------------------------
381 	::rtl::OUString	AssignmentPersistentData::getStringProperty(const sal_Char* _pLocalName) const
382 	{
383 		::rtl::OUString sReturn;
384 		getProperty( _pLocalName ) >>= sReturn;
385 		return sReturn;
386 	}
387 
388 	// -------------------------------------------------------------------
389 	sal_Int32 AssignmentPersistentData::getInt32Property(const sal_Char* _pLocalName) const
390 	{
391 		sal_Int32 nReturn = 0;
392 		getProperty( _pLocalName ) >>= nReturn;
393 		return nReturn;
394 	}
395 
396 	// -------------------------------------------------------------------
397 	void AssignmentPersistentData::setStringProperty(const sal_Char* _pLocalName, const ::rtl::OUString& _rValue)
398 	{
399 		Sequence< ::rtl::OUString > aNames(1);
400 		Sequence< Any > aValues(1);
401 		aNames[0] = ::rtl::OUString::createFromAscii(_pLocalName);
402 		aValues[0] <<= _rValue;
403 		PutProperties(aNames, aValues);
404 	}
405 
406 	// -------------------------------------------------------------------
407 	void AssignmentPersistentData::setFieldAssignment(const ::rtl::OUString& _rLogicalName, const ::rtl::OUString& _rAssignment)
408 	{
409 		if (!_rAssignment.getLength())
410 		{
411 			if (hasFieldAssignment(_rLogicalName))
412 				// the assignment exists but it should be reset
413 				clearFieldAssignment(_rLogicalName);
414 				return;
415 		}
416 
417 		// Fields
418 		::rtl::OUString sDescriptionNodePath(RTL_CONSTASCII_USTRINGPARAM("Fields"));
419 
420 		// Fields/<field>
421 		::rtl::OUString sFieldElementNodePath(sDescriptionNodePath);
422 		sFieldElementNodePath += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/"));
423 		sFieldElementNodePath += _rLogicalName;
424 
425 		Sequence< PropertyValue > aNewFieldDescription(2);
426 		// Fields/<field>/ProgrammaticFieldName
427 		aNewFieldDescription[0].Name = sFieldElementNodePath;
428 		aNewFieldDescription[0].Name += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/ProgrammaticFieldName"));
429 		aNewFieldDescription[0].Value <<= _rLogicalName;
430 		// Fields/<field>/AssignedFieldName
431 		aNewFieldDescription[1].Name = sFieldElementNodePath;
432 		aNewFieldDescription[1].Name += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/AssignedFieldName"));
433 		aNewFieldDescription[1].Value <<= _rAssignment;
434 
435 		// just set the new value
436 #ifdef DBG_UTIL
437 		sal_Bool bSuccess =
438 #endif
439 		SetSetProperties(sDescriptionNodePath, aNewFieldDescription);
440 		DBG_ASSERT(bSuccess, "AssignmentPersistentData::setFieldAssignment: could not commit the changes a field!");
441 	}
442 
443 	// -------------------------------------------------------------------
444 	void AssignmentPersistentData::clearFieldAssignment(const ::rtl::OUString& _rLogicalName)
445 	{
446 		if (!hasFieldAssignment(_rLogicalName))
447 			// nothing to do
448 			return;
449 
450 		::rtl::OUString sDescriptionNodePath(RTL_CONSTASCII_USTRINGPARAM("Fields"));
451 		Sequence< ::rtl::OUString > aNames(&_rLogicalName, 1);
452 		ClearNodeElements(sDescriptionNodePath, aNames);
453 	}
454 
455 	// -------------------------------------------------------------------
456 	::rtl::OUString AssignmentPersistentData::getDatasourceName() const
457 	{
458 		return getStringProperty( "DataSourceName" );
459 	}
460 
461 	// -------------------------------------------------------------------
462 	::rtl::OUString AssignmentPersistentData::getCommand() const
463 	{
464 		return getStringProperty( "Command" );
465 	}
466 
467 	// -------------------------------------------------------------------
468 	void AssignmentPersistentData::setDatasourceName(const ::rtl::OUString& _rName)
469 	{
470 		setStringProperty( "DataSourceName", _rName );
471 	}
472 
473 	// -------------------------------------------------------------------
474 	void AssignmentPersistentData::setCommand(const ::rtl::OUString& _rCommand)
475 	{
476 		setStringProperty( "Command", _rCommand );
477 	}
478 
479 	// -------------------------------------------------------------------
480 	sal_Int32 AssignmentPersistentData::getCommandType() const
481 	{
482 		return getInt32Property( "CommandType" );
483 	}
484 
485 	// ===================================================================
486 	// = AddressBookSourceDialogData
487 	// ===================================================================
488 	struct AddressBookSourceDialogData
489 	{
490 		FixedText*		pFieldLabels[FIELD_PAIRS_VISIBLE * 2];
491 		ListBox*		pFields[FIELD_PAIRS_VISIBLE * 2];
492 
493         /// when working transient, we need the data source
494         Reference< XDataSource >
495                         m_xTransientDataSource;
496 		/// current scroll pos in the field list
497 		sal_Int32		nFieldScrollPos;
498 		/// the index within m_pFields of the last visible list box. This is redundant, it could be extracted from other members
499 		sal_Int32		nLastVisibleListIndex;
500 		/// indicates that we've an odd field number. This member is for efficiency only, it's redundant.
501 		sal_Bool		bOddFieldNumber : 1;
502 		/// indicates that we're working with the real persistent configuration
503 		sal_Bool		bWorkingPersistent : 1;
504 
505 		/// the strings to use as labels for the field selection listboxes
506 		StringArray		aFieldLabels;
507 		// the current field assignment
508 		StringArray		aFieldAssignments;
509 		/// the logical field names
510 		StringArray		aLogicalFieldNames;
511 
512 		IAssigmentData*	pConfigData;
513 
514 		// ................................................................
515 		AddressBookSourceDialogData( )
516 			:nFieldScrollPos(0)
517 			,nLastVisibleListIndex(0)
518 			,bOddFieldNumber(sal_False)
519 			,bWorkingPersistent( sal_True )
520 			,pConfigData( new AssignmentPersistentData )
521 		{
522 		}
523 
524 		// ................................................................
525 		AddressBookSourceDialogData( const Reference< XDataSource >& _rxTransientDS, const ::rtl::OUString& _rDataSourceName,
526             const ::rtl::OUString& _rTableName, const Sequence< AliasProgrammaticPair >& _rFields )
527 			:m_xTransientDataSource( _rxTransientDS )
528             ,nFieldScrollPos(0)
529 			,nLastVisibleListIndex(0)
530 			,bOddFieldNumber(sal_False)
531 			,bWorkingPersistent( sal_False )
532 			,pConfigData( new AssigmentTransientData( m_xTransientDataSource, _rDataSourceName, _rTableName, _rFields ) )
533 		{
534 		}
535 
536 		~AddressBookSourceDialogData()
537 		{
538 			delete pConfigData;
539 		}
540 
541 	};
542 
543 	// ===================================================================
544 	// = AddressBookSourceDialog
545 	// ===================================================================
546 #define INIT_FIELDS()	\
547 		 ModalDialog(_pParent, SvtResId( DLG_ADDRESSBOOKSOURCE ))\
548 		,m_aDatasourceFrame			(this, SvtResId(FL_DATASOURCEFRAME))\
549 		,m_aDatasourceLabel			(this, SvtResId(FT_DATASOURCE))\
550 		,m_aDatasource				(this, SvtResId(CB_DATASOURCE))\
551 		,m_aAdministrateDatasources	(this, SvtResId(PB_ADMINISTATE_DATASOURCES))\
552 		,m_aTableLabel				(this, SvtResId(FT_TABLE))\
553 		,m_aTable					(this, SvtResId(CB_TABLE))\
554 		,m_aFieldsTitle				(this, SvtResId(FT_FIELDS))\
555 		,m_aFieldsFrame				(this, SvtResId(CT_BORDER))\
556 		,m_aFieldScroller			(&m_aFieldsFrame, SvtResId(SB_FIELDSCROLLER))\
557 		,m_aOK						(this, SvtResId(PB_OK))\
558 		,m_aCancel					(this, SvtResId(PB_CANCEL))\
559 		,m_aHelp					(this, SvtResId(PB_HELP))\
560 		,m_sNoFieldSelection(SvtResId(STR_NO_FIELD_SELECTION))\
561 		,m_xORB(_rxORB)
562 
563 	// -------------------------------------------------------------------
564 	AddressBookSourceDialog::AddressBookSourceDialog(Window* _pParent,
565 			const Reference< XMultiServiceFactory >& _rxORB )
566 		:INIT_FIELDS()
567 		,m_pImpl( new AddressBookSourceDialogData )
568 	{
569 		implConstruct();
570 	}
571 
572 	// -------------------------------------------------------------------
573 	AddressBookSourceDialog::AddressBookSourceDialog( Window* _pParent, const Reference< XMultiServiceFactory >& _rxORB,
574 		const Reference< XDataSource >& _rxTransientDS, const ::rtl::OUString& _rDataSourceName,
575         const ::rtl::OUString& _rTable, const Sequence< AliasProgrammaticPair >& _rMapping )
576 		:INIT_FIELDS()
577 		,m_pImpl( new AddressBookSourceDialogData( _rxTransientDS, _rDataSourceName, _rTable, _rMapping ) )
578 	{
579 		implConstruct();
580 	}
581 
582 	// -------------------------------------------------------------------
583 	void AddressBookSourceDialog::implConstruct()
584 	{
585 		for (sal_Int32 row=0; row<FIELD_PAIRS_VISIBLE; ++row)
586 		{
587 			for (sal_Int32 column=0; column<2; ++column)
588 			{
589 				// the label
590 				m_pImpl->pFieldLabels[row * 2 + column] = new FixedText(&m_aFieldsFrame, SvtResId((sal_uInt16)(FT_FIELD_BASE + row * 2 + column)));
591 				// the listbox
592 				m_pImpl->pFields[row * 2 + column] = new ListBox(&m_aFieldsFrame, SvtResId((sal_uInt16)(LB_FIELD_BASE + row * 2 + column)));
593 				m_pImpl->pFields[row * 2 + column]->SetDropDownLineCount(15);
594 				m_pImpl->pFields[row * 2 + column]->SetSelectHdl(LINK(this, AddressBookSourceDialog, OnFieldSelect));
595 
596 				m_pImpl->pFields[row * 2 + column]->SetHelpId(HID_ADDRTEMPL_FIELD_ASSIGNMENT);
597 			}
598 		}
599 
600 		m_aFieldsFrame.SetStyle((m_aFieldsFrame.GetStyle() | WB_TABSTOP | WB_DIALOGCONTROL) & ~WB_NODIALOGCONTROL);
601 
602 		// correct the z-order
603 		m_aFieldScroller.SetZOrder(m_pImpl->pFields[FIELD_CONTROLS_VISIBLE - 1], WINDOW_ZORDER_BEHIND);
604 		m_aOK.SetZOrder(&m_aFieldsFrame, WINDOW_ZORDER_BEHIND);
605 		m_aCancel.SetZOrder(&m_aOK, WINDOW_ZORDER_BEHIND);
606 
607 		initializeDatasources();
608 
609 		// for the moment, we have a hard coded list of all known fields.
610 		// A better solution would be to store all known field translations in the configuration, which could be
611 		// extensible by the user in an arbitrary way.
612 		// But for the moment we need a quick solution ...
613 		// (the main thing would be to store the translations to use here in the user interface, besides that, the code
614 		// should be adjustable with a rather small effort.)
615 
616 		// initialize the strings for the field labels
617 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_FIRSTNAME )) );
618 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_LASTNAME )) );
619 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_COMPANY)) );
620 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_DEPARTMENT )) );
621 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_STREET )) );
622 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_ZIPCODE )) );
623 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_CITY )) );
624 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_STATE)) );
625 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_COUNTRY )) );
626 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_HOMETEL )) );
627 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_WORKTEL )) );
628 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_OFFICETEL)) );
629 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_MOBILE)) );
630 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_TELOTHER)) );
631 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_PAGER)) );
632 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_FAX )) );
633 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_EMAIL )) );
634 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_URL )) );
635 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_TITLE )) );
636 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_POSITION )) );
637 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_INITIALS )) );
638 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_ADDRFORM )) );
639 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_SALUTATION )) );
640 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_ID)) );
641 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_CALENDAR)) );
642 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_INVITE)) );
643 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_NOTE)) );
644 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_USER1)) );
645 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_USER2)) );
646 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_USER3)) );
647 		m_pImpl->aFieldLabels.push_back( String(SvtResId( STR_FIELD_USER4)) );
648 
649 		// force a even number of known fields
650 		m_pImpl->bOddFieldNumber = (m_pImpl->aFieldLabels.size() % 2) != 0;
651 		if (m_pImpl->bOddFieldNumber)
652 			m_pImpl->aFieldLabels.push_back( String() );
653 
654 		// limit the scrollbar range accordingly
655 		sal_Int32 nOverallFieldPairs = m_pImpl->aFieldLabels.size() / 2;
656 		m_aFieldScroller.SetRange( Range(0, nOverallFieldPairs - FIELD_PAIRS_VISIBLE) );
657 		m_aFieldScroller.SetLineSize(1);
658 		m_aFieldScroller.SetPageSize(FIELD_PAIRS_VISIBLE);
659 
660 		// reset the current field assignments
661 		m_pImpl->aFieldAssignments.resize(m_pImpl->aFieldLabels.size());
662 			// (empty strings mean "no assignment")
663 
664 		// some knittings
665 		m_aFieldScroller.SetScrollHdl(LINK(this, AddressBookSourceDialog, OnFieldScroll));
666 		m_aAdministrateDatasources.SetClickHdl(LINK(this, AddressBookSourceDialog, OnAdministrateDatasources));
667 		m_aDatasource.EnableAutocomplete(sal_True);
668 		m_aTable.EnableAutocomplete(sal_True);
669 		m_aTable.SetGetFocusHdl(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
670 		m_aDatasource.SetGetFocusHdl(LINK(this, AddressBookSourceDialog, OnComboGetFocus));
671 		m_aTable.SetLoseFocusHdl(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
672 		m_aDatasource.SetLoseFocusHdl(LINK(this, AddressBookSourceDialog, OnComboLoseFocus));
673 		m_aTable.SetSelectHdl(LINK(this, AddressBookSourceDialog, OnComboSelect));
674 		m_aDatasource.SetSelectHdl(LINK(this, AddressBookSourceDialog, OnComboSelect));
675 		m_aOK.SetClickHdl(LINK(this, AddressBookSourceDialog, OnOkClicked));
676 
677 		m_aDatasource.SetDropDownLineCount(15);
678 
679 		// initialize the field controls
680 		resetFields();
681 		m_aFieldScroller.SetThumbPos(0);
682 		m_pImpl->nFieldScrollPos = -1;
683 		implScrollFields(0, sal_False, sal_False);
684 
685 		// the logical names
686 		String sLogicalFieldNames(SvtResId(STR_LOCAGICAL_FIELD_NAMES));
687 		sal_Int32 nAdjustedTokenCount = sLogicalFieldNames.GetTokenCount(';') + (m_pImpl->bOddFieldNumber ? 1 : 0);
688 		DBG_ASSERT(nAdjustedTokenCount == (sal_Int32)m_pImpl->aFieldLabels.size(),
689 			"AddressBookSourceDialog::AddressBookSourceDialog: inconsistence between logical and UI field names!");
690 		m_pImpl->aLogicalFieldNames.reserve(nAdjustedTokenCount);
691 		for (sal_Int32 i = 0; i<nAdjustedTokenCount; ++i)
692 			m_pImpl->aLogicalFieldNames.push_back(sLogicalFieldNames.GetToken((sal_uInt16)i, ';'));
693 
694 		PostUserEvent(LINK(this, AddressBookSourceDialog, OnDelayedInitialize));
695 			// so the dialog will at least show up before we do the loading of the
696 			// configuration data and the (maybe time consuming) analysis of the data source/table to select
697 
698 		FreeResource();
699 
700 		if ( !m_pImpl->bWorkingPersistent )
701 		{
702 			StyleSettings aSystemStyle = GetSettings().GetStyleSettings();
703 			const Color& rNewColor = aSystemStyle.GetDialogColor();
704 
705 			m_aDatasource.SetReadOnly( sal_True );
706 			m_aDatasource.SetBackground( Wallpaper( rNewColor ) );
707 			m_aDatasource.SetControlBackground( rNewColor );
708 
709 			m_aTable.SetReadOnly( sal_True );
710 			m_aTable.SetBackground( Wallpaper( rNewColor ) );
711 			m_aTable.SetControlBackground( rNewColor );
712 
713 			m_aAdministrateDatasources.Hide( );
714 		}
715 	}
716 
717 	// -------------------------------------------------------------------
718 	void AddressBookSourceDialog::getFieldMapping(Sequence< AliasProgrammaticPair >& _rMapping) const
719 	{
720 		_rMapping.realloc( m_pImpl->aLogicalFieldNames.size() );
721 		AliasProgrammaticPair* pPair = _rMapping.getArray();
722 
723 		::rtl::OUString sCurrent;
724 		for	(	ConstStringArrayIterator aProgrammatic = m_pImpl->aLogicalFieldNames.begin();
725 				aProgrammatic != m_pImpl->aLogicalFieldNames.end();
726 				++aProgrammatic
727 			)
728 		{
729 			sCurrent = *aProgrammatic;
730 			if ( m_pImpl->pConfigData->hasFieldAssignment( sCurrent ) )
731 			{
732 				// the user gave us an assignment for this field
733 				pPair->ProgrammaticName = *aProgrammatic;
734 				pPair->Alias = m_pImpl->pConfigData->getFieldAssignment( *aProgrammatic );
735 				++pPair;
736 			}
737 		}
738 
739 		_rMapping.realloc( pPair - _rMapping.getArray() );
740 	}
741 
742 	// -------------------------------------------------------------------
743 	void AddressBookSourceDialog::loadConfiguration()
744 	{
745         ::rtl::OUString sName = m_pImpl->pConfigData->getDatasourceName();
746         INetURLObject aURL( sName );
747         if( aURL.GetProtocol() != INET_PROT_NOT_VALID )
748 		{
749 			OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::NO_DECODE ) );
750 			sName = aFileNotation.get(OFileNotation::N_SYSTEM);
751         }
752 
753 		m_aDatasource.SetText(sName);
754 		m_aTable.SetText(m_pImpl->pConfigData->getCommand());
755 		// we ignore the CommandType: only tables are supported
756 
757 		// the logical names for the fields
758 		DBG_ASSERT(m_pImpl->aLogicalFieldNames.size() == m_pImpl->aFieldAssignments.size(),
759 			"AddressBookSourceDialog::loadConfiguration: inconsistence between field names and field assignments!");
760 
761 		ConstStringArrayIterator aLogical = m_pImpl->aLogicalFieldNames.begin();
762 		StringArrayIterator aAssignment = m_pImpl->aFieldAssignments.begin();
763 		for	(	;
764 				aLogical < m_pImpl->aLogicalFieldNames.end();
765 				++aLogical, ++aAssignment
766 			)
767 			*aAssignment = m_pImpl->pConfigData->getFieldAssignment(*aLogical);
768 	}
769 
770 	// -------------------------------------------------------------------
771 	AddressBookSourceDialog::~AddressBookSourceDialog()
772 	{
773 		sal_Int32 i;
774 		for (i=0; i<FIELD_CONTROLS_VISIBLE; ++i)
775 		{
776 			delete m_pImpl->pFieldLabels[i];
777 			delete m_pImpl->pFields[i];
778 		}
779 
780 		delete m_pImpl;
781 	}
782 
783 	// -------------------------------------------------------------------
784 	void AddressBookSourceDialog::initializeDatasources()
785 	{
786 		if (!m_xDatabaseContext.is())
787 		{
788 			DBG_ASSERT(m_xORB.is(), "AddressBookSourceDialog::initializeDatasources: no service factory!");
789 			if (!m_xORB.is())
790 				return;
791 
792 			const String sContextServiceName = String::CreateFromAscii("com.sun.star.sdb.DatabaseContext");
793 			try
794 			{
795 				m_xDatabaseContext = Reference< XNameAccess >(m_xORB->createInstance(sContextServiceName), UNO_QUERY);
796 			}
797 			catch(Exception&) { }
798 			if (!m_xDatabaseContext.is())
799 			{
800 				ShowServiceNotAvailableError( this, sContextServiceName, sal_False);
801 				return;
802 			}
803 		}
804 		m_aDatasource.Clear();
805 
806 		// fill the datasources listbox
807 		Sequence< ::rtl::OUString > aDatasourceNames;
808 		try
809 		{
810 			aDatasourceNames = m_xDatabaseContext->getElementNames();
811 		}
812 		catch(Exception&)
813 		{
814 			DBG_ERROR("AddressBookSourceDialog::initializeDatasources: caught an exception while asking for the data source names!");
815 		}
816 		const ::rtl::OUString* pDatasourceNames = aDatasourceNames.getConstArray();
817 		const ::rtl::OUString* pEnd = pDatasourceNames + aDatasourceNames.getLength();
818 		for (; pDatasourceNames < pEnd; ++pDatasourceNames)
819 			m_aDatasource.InsertEntry(*pDatasourceNames);
820 	}
821 
822 	// -------------------------------------------------------------------
823 	IMPL_LINK(AddressBookSourceDialog, OnFieldScroll, ScrollBar*, _pScrollBar)
824 	{
825 		implScrollFields( _pScrollBar->GetThumbPos(), sal_True, sal_True );
826 		return 0L;
827 	}
828 
829 	// -------------------------------------------------------------------
830 	void AddressBookSourceDialog::resetTables()
831 	{
832 		if (!m_xDatabaseContext.is())
833 			return;
834 
835 		WaitObject aWaitCursor(this);
836 
837 		// no matter what we do here, we handled the currently selected data source (no matter if successfull or not)
838 		m_aDatasource.SaveValue();
839 
840 		// create an interaction handler (may be needed for connecting)
841 		const String sInteractionHandlerServiceName = String::CreateFromAscii("com.sun.star.task.InteractionHandler");
842 		Reference< XInteractionHandler > xHandler;
843 		try
844 		{
845 			xHandler = Reference< XInteractionHandler >(m_xORB->createInstance(sInteractionHandlerServiceName), UNO_QUERY);
846 		}
847 		catch(Exception&) { }
848 		if (!xHandler.is())
849 		{
850 			ShowServiceNotAvailableError(this, sInteractionHandlerServiceName, sal_True);
851 			return;
852 		}
853 
854 		// the currently selected table
855 		::rtl::OUString sOldTable = m_aTable.GetText();
856 
857 		m_aTable.Clear();
858 
859 		m_xCurrentDatasourceTables= NULL;
860 
861 		// get the tables of the connection
862 		Sequence< ::rtl::OUString > aTableNames;
863 		Any aException;
864 		try
865 		{
866 			Reference< XCompletedConnection > xDS;
867             if ( m_pImpl->bWorkingPersistent )
868             {
869                 String sSelectedDS = lcl_getSelectedDataSource(  m_aDatasource );
870 
871 			    // get the data source the user has chosen and let it build a connection
872                 INetURLObject aURL( sSelectedDS );
873                 if ( aURL.GetProtocol() != INET_PROT_NOT_VALID || m_xDatabaseContext->hasByName(sSelectedDS) )
874 				    m_xDatabaseContext->getByName( sSelectedDS ) >>= xDS;
875             }
876             else
877             {
878                 xDS = xDS.query( m_pImpl->m_xTransientDataSource );
879             }
880 
881 			// build the connection
882 			Reference< XConnection > xConn;
883             if (xDS.is())
884 				xConn = xDS->connectWithCompletion(xHandler);
885 
886 			// get the table names
887 			Reference< XTablesSupplier > xSupplTables(xConn, UNO_QUERY);
888 			if (xSupplTables.is())
889 			{
890 				m_xCurrentDatasourceTables = Reference< XNameAccess >(xSupplTables->getTables(), UNO_QUERY);
891 				if (m_xCurrentDatasourceTables.is())
892 					aTableNames = m_xCurrentDatasourceTables->getElementNames();
893 			}
894 		}
895 		catch(SQLContext& e) { aException <<= e; }
896 		catch(SQLWarning& e) { aException <<= e; }
897 		catch(SQLException& e) { aException <<= e; }
898 		catch(Exception&)
899 		{
900 			DBG_ERROR("AddressBookSourceDialog::resetTables: could not retrieve the table!");
901 		}
902 
903 		if (aException.hasValue())
904 		{
905 			Reference< XInteractionRequest > xRequest = new OInteractionRequest(aException);
906 			try
907 			{
908 				xHandler->handle(xRequest);
909 			}
910 			catch(Exception&) { }
911 			return;
912 		}
913 
914 		sal_Bool bKnowOldTable = sal_False;
915 		// fill the table list
916 		const ::rtl::OUString* pTableNames = aTableNames.getConstArray();
917 		const ::rtl::OUString* pEnd = pTableNames + aTableNames.getLength();
918 		for (;pTableNames != pEnd; ++pTableNames)
919 		{
920 			m_aTable.InsertEntry(*pTableNames);
921 			if (0 == pTableNames->compareTo(sOldTable))
922 				bKnowOldTable = sal_True;
923 		}
924 
925 		// set the old table, if the new data source knows a table with this name, too. Else reset the tables edit field.
926 		if (!bKnowOldTable)
927 			sOldTable = ::rtl::OUString();
928 		m_aTable.SetText(sOldTable);
929 
930 		resetFields();
931 	}
932 
933 	// -------------------------------------------------------------------
934 	void AddressBookSourceDialog::resetFields()
935 	{
936 		WaitObject aWaitCursor(this);
937 
938 		// no matter what we do here, we handled the currently selected table (no matter if successfull or not)
939 		m_aDatasource.SaveValue();
940 
941 		String sSelectedTable = m_aTable.GetText();
942 		Sequence< ::rtl::OUString > aColumnNames;
943 		try
944 		{
945 			if (m_xCurrentDatasourceTables.is())
946 			{
947 				// get the table and the columns
948 				Reference< XColumnsSupplier > xSuppTableCols;
949 				if (m_xCurrentDatasourceTables->hasByName(sSelectedTable))
950 					::cppu::extractInterface(xSuppTableCols, m_xCurrentDatasourceTables->getByName(sSelectedTable));
951 				Reference< XNameAccess > xColumns;
952 				if (xSuppTableCols.is())
953 					xColumns = xSuppTableCols->getColumns();
954 				if (xColumns.is())
955 					aColumnNames = xColumns->getElementNames();
956 			}
957 		}
958 		catch(Exception&)
959 		{
960 			DBG_ERROR("AddressBookSourceDialog::resetFields: could not retrieve the table columns!");
961 		}
962 
963 
964 		const ::rtl::OUString* pColumnNames = aColumnNames.getConstArray();
965 		const ::rtl::OUString* pEnd = pColumnNames + aColumnNames.getLength();
966 
967 		// for quicker access
968 		::std::set< String > aColumnNameSet;
969 		for (pColumnNames = aColumnNames.getConstArray(); pColumnNames != pEnd; ++pColumnNames)
970 			aColumnNameSet.insert(*pColumnNames);
971 
972 		std::vector<String>::iterator aInitialSelection = m_pImpl->aFieldAssignments.begin() + m_pImpl->nFieldScrollPos;
973 
974 		ListBox** pListbox = m_pImpl->pFields;
975 		String sSaveSelection;
976 		for (sal_Int32 i=0; i<FIELD_CONTROLS_VISIBLE; ++i, ++pListbox, ++aInitialSelection)
977 		{
978 			sSaveSelection = (*pListbox)->GetSelectEntry();
979 
980 			(*pListbox)->Clear();
981 
982 			// the one entry for "no selection"
983 			(*pListbox)->InsertEntry(m_sNoFieldSelection, 0);
984 			// as it's entry data, set the index of the list box in our array
985 			(*pListbox)->SetEntryData(0, reinterpret_cast<void*>(i));
986 
987 			// the field names
988 			for (pColumnNames = aColumnNames.getConstArray(); pColumnNames != pEnd; ++pColumnNames)
989 				(*pListbox)->InsertEntry(*pColumnNames);
990 
991 			if (aInitialSelection->Len() && (aColumnNameSet.end() != aColumnNameSet.find(*aInitialSelection)))
992 				// we can select the entry as specified in our field assignment array
993 				(*pListbox)->SelectEntry(*aInitialSelection);
994 			else
995 				// try to restore the selection
996 				if (aColumnNameSet.end() != aColumnNameSet.find(sSaveSelection))
997 					// the old selection is a valid column name
998 					(*pListbox)->SelectEntry(sSaveSelection);
999 				else
1000 					// select the <none> entry
1001 					(*pListbox)->SelectEntryPos(0);
1002 		}
1003 
1004 		// adjust m_pImpl->aFieldAssignments
1005 		for (	StringArrayIterator aAdjust = m_pImpl->aFieldAssignments.begin();
1006 				aAdjust != m_pImpl->aFieldAssignments.end();
1007 				++aAdjust
1008 			)
1009 			if (aAdjust->Len())
1010 				if (aColumnNameSet.end() == aColumnNameSet.find(*aAdjust))
1011 					aAdjust->Erase();
1012 	}
1013 
1014 	// -------------------------------------------------------------------
1015 	IMPL_LINK(AddressBookSourceDialog, OnFieldSelect, ListBox*, _pListbox)
1016 	{
1017 		// the index of the affected list box in our array
1018 		sal_IntPtr nListBoxIndex = reinterpret_cast<sal_IntPtr>(_pListbox->GetEntryData(0));
1019 		DBG_ASSERT(nListBoxIndex >= 0 && nListBoxIndex < FIELD_CONTROLS_VISIBLE,
1020 			"AddressBookSourceDialog::OnFieldScroll: invalid list box entry!");
1021 
1022 		// update the array where we remember the field selections
1023 		if (0 == _pListbox->GetSelectEntryPos())
1024 			// it's the "no field selection" entry
1025 			m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex] = String();
1026 		else
1027 			// it's a regular field entry
1028 			m_pImpl->aFieldAssignments[m_pImpl->nFieldScrollPos * 2 + nListBoxIndex] = _pListbox->GetSelectEntry();
1029 
1030 		return 0L;
1031 	}
1032 
1033 	// -------------------------------------------------------------------
1034 	void AddressBookSourceDialog::implScrollFields(sal_Int32 _nPos, sal_Bool _bAdjustFocus, sal_Bool _bAdjustScrollbar)
1035 	{
1036 		if (_nPos == m_pImpl->nFieldScrollPos)
1037 			// nothing to do
1038 			return;
1039 
1040 		// loop through our field control rows and do some adjustments
1041 		// for the new texts
1042 		FixedText** pLeftLabelControl = m_pImpl->pFieldLabels;
1043 		FixedText** pRightLabelControl = pLeftLabelControl + 1;
1044 		ConstStringArrayIterator pLeftColumnLabel = m_pImpl->aFieldLabels.begin() + 2 * _nPos;
1045 		ConstStringArrayIterator pRightColumnLabel = pLeftColumnLabel + 1;
1046 
1047 		// for the focus movement and the selection scroll
1048 		ListBox** pLeftListControl = m_pImpl->pFields;
1049 		ListBox** pRightListControl = pLeftListControl + 1;
1050 
1051 		// for the focus movement
1052 		sal_Int32 nOldFocusRow = -1;
1053 		sal_Int32 nOldFocusColumn = 0;
1054 
1055 		// for the selection scroll
1056 		ConstStringArrayIterator pLeftAssignment = m_pImpl->aFieldAssignments.begin() + 2 * _nPos;
1057 		ConstStringArrayIterator pRightAssignment = pLeftAssignment + 1;
1058 
1059 		m_pImpl->nLastVisibleListIndex = -1;
1060 		// loop
1061 		for (sal_Int32 i=0; i<FIELD_PAIRS_VISIBLE; ++i)
1062 		{
1063 			if ((*pLeftListControl)->HasChildPathFocus())
1064 			{
1065 				nOldFocusRow = i;
1066 				nOldFocusColumn = 0;
1067 			}
1068 			else if ((*pRightListControl)->HasChildPathFocus())
1069 			{
1070 				nOldFocusRow = i;
1071 				nOldFocusColumn = 1;
1072 			}
1073 
1074 			// the new texts of the label controls
1075 			(*pLeftLabelControl)->SetText(*pLeftColumnLabel);
1076 			(*pRightLabelControl)->SetText(*pRightColumnLabel);
1077 
1078 			// we may have to hide the controls in the right column, if we have no label text for it
1079 			// (which means we have an odd number of fields, though we forced our internal arrays to
1080 			// be even-sized for easier handling)
1081 			// (If sometimes we support an arbitrary number of field assignments, we would have to care for
1082 			// an invisible left hand side column, too. But right now, the left hand side controls are always
1083 			// visible)
1084 			sal_Bool bHideRightColumn = (0 == pRightColumnLabel->Len());
1085 			(*pRightLabelControl)->Show(!bHideRightColumn);
1086 			(*pRightListControl)->Show(!bHideRightColumn);
1087 			// the new selections of the listboxes
1088 			implSelectField(*pLeftListControl, *pLeftAssignment);
1089 			implSelectField(*pRightListControl, *pRightAssignment);
1090 
1091 			// the index of the last visible list box
1092 			++m_pImpl->nLastVisibleListIndex;	// the left hand side box is always visible
1093 			if (!bHideRightColumn)
1094 				++m_pImpl->nLastVisibleListIndex;
1095 
1096 		    // increment ...
1097             if ( i < FIELD_PAIRS_VISIBLE - 1 )
1098             {   // (not in the very last round, here the +=2 could result in an invalid
1099                 // iterator position, which causes an abort in a non-product version
1100 			    pLeftLabelControl += 2;
1101 			    pRightLabelControl += 2;
1102 			    pLeftColumnLabel += 2;
1103 			    pRightColumnLabel += 2;
1104 
1105 			    pLeftListControl += 2;
1106 			    pRightListControl += 2;
1107 			    pLeftAssignment += 2;
1108 			    pRightAssignment += 2;
1109             }
1110 		}
1111 
1112 		if (_bAdjustFocus && (nOldFocusRow >= 0))
1113 		{	// we have to adjust the focus and one of the list boxes has the focus
1114 			sal_Int32 nDelta = m_pImpl->nFieldScrollPos - _nPos;
1115 			// the new row for the focus
1116 			sal_Int32 nNewFocusRow = nOldFocusRow + nDelta;
1117 			// normalize
1118 			nNewFocusRow = std::min(nNewFocusRow, (sal_Int32)(FIELD_PAIRS_VISIBLE - 1), ::std::less< sal_Int32 >());
1119 			nNewFocusRow = std::max(nNewFocusRow, (sal_Int32)0, ::std::less< sal_Int32 >());
1120 			// set the new focus (in the same column)
1121 			m_pImpl->pFields[nNewFocusRow * 2 + nOldFocusColumn]->GrabFocus();
1122 		}
1123 
1124 		m_pImpl->nFieldScrollPos = _nPos;
1125 
1126 		if (_bAdjustScrollbar)
1127 			m_aFieldScroller.SetThumbPos(m_pImpl->nFieldScrollPos);
1128 	}
1129 
1130 	// -------------------------------------------------------------------
1131 	void AddressBookSourceDialog::implSelectField(ListBox* _pBox, const String& _rText)
1132 	{
1133 		if (_rText.Len())
1134 			// a valid field name
1135 			_pBox->SelectEntry(_rText);
1136 		else
1137 			// no selection for this item
1138 			_pBox->SelectEntryPos(0);
1139 	}
1140 
1141 	// -------------------------------------------------------------------
1142 	IMPL_LINK(AddressBookSourceDialog, OnDelayedInitialize, void*, EMPTYARG)
1143 	{
1144 		// load the initial data from the configuration
1145 		loadConfiguration();
1146 		resetTables();
1147 			// will reset the tables/fields implicitly
1148 
1149 		if ( !m_pImpl->bWorkingPersistent )
1150 			if ( m_pImpl->pFields[0] )
1151 				m_pImpl->pFields[0]->GrabFocus();
1152 
1153 		return 0L;
1154 	}
1155 
1156 	// -------------------------------------------------------------------
1157 	IMPL_LINK(AddressBookSourceDialog, OnComboSelect, ComboBox*, _pBox)
1158 	{
1159 		if (_pBox == &m_aDatasource)
1160 			resetTables();
1161 		else
1162 			resetFields();
1163 		return 0;
1164 	}
1165 
1166 	// -------------------------------------------------------------------
1167 	IMPL_LINK(AddressBookSourceDialog, OnComboGetFocus, ComboBox*, _pBox)
1168 	{
1169 		_pBox->SaveValue();
1170 		return 0L;
1171 	}
1172 
1173 	// -------------------------------------------------------------------
1174 	IMPL_LINK(AddressBookSourceDialog, OnComboLoseFocus, ComboBox*, _pBox)
1175 	{
1176 		if (_pBox->GetSavedValue() != _pBox->GetText())
1177 		{
1178 			if (_pBox == &m_aDatasource)
1179 				resetTables();
1180 			else
1181 				resetFields();
1182 		}
1183 		return 0L;
1184 	}
1185 
1186 	// -------------------------------------------------------------------
1187 	IMPL_LINK(AddressBookSourceDialog, OnOkClicked, Button*, EMPTYARG)
1188 	{
1189         String sSelectedDS = lcl_getSelectedDataSource(  m_aDatasource );
1190         if ( m_pImpl->bWorkingPersistent )
1191         {
1192 		    m_pImpl->pConfigData->setDatasourceName(sSelectedDS);
1193 		    m_pImpl->pConfigData->setCommand(m_aTable.GetText());
1194         }
1195 
1196 		// set the field assignments
1197 		ConstStringArrayIterator aLogical = m_pImpl->aLogicalFieldNames.begin();
1198 		ConstStringArrayIterator aAssignment = m_pImpl->aFieldAssignments.begin();
1199 		for	(	;
1200 				aLogical < m_pImpl->aLogicalFieldNames.end();
1201 				++aLogical, ++aAssignment
1202 			)
1203 			m_pImpl->pConfigData->setFieldAssignment(*aLogical, *aAssignment);
1204 
1205 
1206 		EndDialog(RET_OK);
1207 		return 0L;
1208 	}
1209 
1210 	// -------------------------------------------------------------------
1211 	IMPL_LINK(AddressBookSourceDialog, OnAdministrateDatasources, void*, EMPTYARG)
1212 	{
1213 		// collect some initial arguments for the dialog
1214 		Sequence< Any > aArgs(1);
1215         aArgs[0] <<= PropertyValue(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParentWindow")), 0, makeAny(VCLUnoHelper::GetInterface(this)), PropertyState_DIRECT_VALUE);
1216 
1217 		// create the dialog object
1218 		const String sDialogServiceName = String::CreateFromAscii("com.sun.star.ui.dialogs.AddressBookSourcePilot");
1219 		Reference< XExecutableDialog > xAdminDialog;
1220 		try
1221 		{
1222 			xAdminDialog = Reference< XExecutableDialog >(m_xORB->createInstanceWithArguments(sDialogServiceName, aArgs), UNO_QUERY);
1223 		}
1224 		catch(Exception&) { }
1225 		if (!xAdminDialog.is())
1226 		{
1227 			ShowServiceNotAvailableError(this, sDialogServiceName, sal_True);
1228 			return 1L;
1229 		}
1230 
1231 		// excute the dialog
1232 		try
1233 		{
1234             if ( xAdminDialog->execute() == RET_OK )
1235             {
1236                 Reference<XPropertySet> xProp(xAdminDialog,UNO_QUERY);
1237 				if ( xProp.is() )
1238 				{
1239                     ::rtl::OUString sName;
1240                     xProp->getPropertyValue(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("DataSourceName"))) >>= sName;
1241 
1242                     INetURLObject aURL( sName );
1243                     if( aURL.GetProtocol() != INET_PROT_NOT_VALID )
1244 			        {
1245 				        OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::NO_DECODE ) );
1246 				        sName = aFileNotation.get(OFileNotation::N_SYSTEM);
1247                     }
1248                     m_aDatasource.InsertEntry(sName);
1249 					delete m_pImpl->pConfigData;
1250 					m_pImpl->pConfigData = new AssignmentPersistentData();
1251 					loadConfiguration();
1252                     resetTables();
1253 			        // will reset the fields implicitly
1254                 }
1255             }
1256 		}
1257 		catch(Exception&)
1258 		{
1259 			DBG_ERROR("AddressBookSourceDialog::OnAdministrateDatasources: an error occured while executing the administration dialog!");
1260 		}
1261 
1262 		// re-fill the data source list
1263 		// try to preserve the current selection
1264 
1265 //		initializeDatasources();
1266 
1267 		return 0L;
1268 	}
1269 
1270 	// -------------------------------------------------------------------
1271 	long AddressBookSourceDialog::PreNotify( NotifyEvent& _rNEvt )
1272 	{
1273 		switch (_rNEvt.GetType())
1274 		{
1275 			case EVENT_KEYINPUT:
1276 			{
1277 				const KeyEvent* pKeyEvent =	_rNEvt.GetKeyEvent();
1278 				sal_uInt16 nCode  = pKeyEvent->GetKeyCode().GetCode();
1279 				sal_Bool   bShift = pKeyEvent->GetKeyCode().IsShift();
1280 				sal_Bool   bCtrl  = pKeyEvent->GetKeyCode().IsMod1();
1281 				sal_Bool   bAlt =	pKeyEvent->GetKeyCode().IsMod2();
1282 
1283 				if (KEY_TAB == nCode)
1284 				{	// somebody pressed the tab key
1285 					if (!bAlt && !bCtrl && !bShift)
1286 					{	// it's really the only the key (no modifiers)
1287 						if (m_pImpl->pFields[m_pImpl->nLastVisibleListIndex]->HasChildPathFocus())
1288 							// the last of our visible list boxes has the focus
1289 							if (m_pImpl->nFieldScrollPos < m_aFieldScroller.GetRangeMax())
1290 							{	// we can still scroll down
1291 								sal_Int32 nNextFocusList = m_pImpl->nLastVisibleListIndex + 1 - 2;
1292 								// -> scroll down
1293 								implScrollFields(m_pImpl->nFieldScrollPos + 1, sal_False, sal_True);
1294 								// give the left control in the "next" line the focus
1295 								m_pImpl->pFields[nNextFocusList]->GrabFocus();
1296 								// return saying "have handled this"
1297 								return 1;
1298 							}
1299 					}
1300 					else if (!bAlt && !bCtrl && bShift)
1301 					{	// it's shift-tab
1302 						if (m_pImpl->pFields[0]->HasChildPathFocus())
1303 							// our first list box has the focus
1304 							if (m_pImpl->nFieldScrollPos > 0)
1305 							{	// we can still scroll up
1306 								// -> scroll up
1307 								implScrollFields(m_pImpl->nFieldScrollPos - 1, sal_False, sal_True);
1308 								// give the right control in the "prebious" line the focus
1309 								m_pImpl->pFields[0 - 1 + 2]->GrabFocus();
1310 								// return saying "have handled this"
1311 								return 1;
1312 							}
1313 					}
1314 				}
1315 			}
1316 			break;
1317 		}
1318 		return ModalDialog::PreNotify(_rNEvt);
1319 	}
1320 
1321 // .......................................................................
1322 }	// namespace svt
1323 // .......................................................................
1324 
1325