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