xref: /trunk/main/dbaccess/source/ui/dlg/dbfindex.cxx (revision 96de5490)
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_dbaccess.hxx"
26 
27 #ifndef _DBAUI_DBFINDEX_HXX_
28 #include "dbfindex.hxx"
29 #endif
30 
31 #ifndef _CONFIG_HXX
32 #include <tools/config.hxx>
33 #endif
34 #ifndef _SFXAPP_HXX //autogen
35 #include <sfx2/app.hxx>
36 #endif
37 #ifndef _DBAUI_MODULE_DBU_HXX_
38 #include "moduledbu.hxx"
39 #endif
40 #ifndef _DBU_DLG_HRC_
41 #include "dbu_dlg.hrc"
42 #endif
43 #ifndef _DBAUI_DBF_INDEXES_HRC_
44 #include "dbfindex.hrc"
45 #endif
46 #ifndef _TOOLS_DEBUG_HXX
47 #include <tools/debug.hxx>
48 #endif
49 #ifndef _UNOTOOLS_LOCALFILEHELPER_HXX
50 #include <unotools/localfilehelper.hxx>
51 #endif
52 #ifndef _URLOBJ_HXX
53 #include <tools/urlobj.hxx>
54 #endif
55 #ifndef INCLUDED_SVTOOLS_PATHOPTIONS_HXX
56 #include <unotools/pathoptions.hxx>
57 #endif
58 #ifndef _UCBHELPER_CONTENT_HXX
59 #include <ucbhelper/content.hxx>
60 #endif
61 #ifndef SVTOOLS_FILENOTATION_HXX_
62 #include <svl/filenotation.hxx>
63 #endif
64 
65 
66 //.........................................................................
67 namespace dbaui
68 {
69 //.........................................................................
70 
71 using namespace ::com::sun::star::uno;
72 using namespace ::com::sun::star::ucb;
73 using namespace ::svt;
74 
75 const ByteString aGroupIdent("dBase III");
76 
77 //////////////////////////////////////////////////////////////////////////
78 // Klasse ODbaseIndexDialog
79 DBG_NAME(ODbaseIndexDialog)
80 //-------------------------------------------------------------------------
81 ODbaseIndexDialog::ODbaseIndexDialog( Window * pParent, String aDataSrcName )
82 	: ModalDialog( pParent, ModuleRes(DLG_DBASE_INDEXES) ),
83 	aPB_OK(				this, ModuleRes( PB_OK ) ),
84 	aPB_CANCEL(			this, ModuleRes( PB_CANCEL ) ),
85 	aPB_HELP(			this, ModuleRes( PB_HELP ) ),
86 	m_FT_Tables(		this, ModuleRes( FT_TABLES ) ),
87 	aCB_Tables(			this, ModuleRes( CB_TABLES ) ),
88     m_FL_Indexes(       this, ModuleRes( FL_INDEXES ) ),
89 	m_FT_TableIndexes(	this, ModuleRes( FT_TABLEINDEXES ) ),
90 	aLB_TableIndexes(	this, ModuleRes( LB_TABLEINDEXES ) ),
91 	m_FT_AllIndexes(	this, ModuleRes( FT_ALLINDEXES ) ),
92 	aLB_FreeIndexes(	this, ModuleRes( LB_FREEINDEXES ) ),
93     aIB_Add(            this, ModuleRes( IB_ADD ) ),
94     aIB_Remove(         this, ModuleRes( IB_REMOVE ) ),
95     aIB_AddAll(         this, ModuleRes( IB_ADDALL ) ),
96     aIB_RemoveAll(      this, ModuleRes( IB_REMOVEALL ) ),
97 	m_aDSN(aDataSrcName),
98 	m_bCaseSensitiv(sal_True)
99 {
100     DBG_CTOR(ODbaseIndexDialog,NULL);
101 
102 	aCB_Tables.SetSelectHdl( LINK(this, ODbaseIndexDialog, TableSelectHdl) );
103     aIB_Add.SetClickHdl( LINK(this, ODbaseIndexDialog, AddClickHdl) );
104     aIB_Remove.SetClickHdl( LINK(this, ODbaseIndexDialog, RemoveClickHdl) );
105     aIB_AddAll.SetClickHdl( LINK(this, ODbaseIndexDialog, AddAllClickHdl) );
106     aIB_RemoveAll.SetClickHdl( LINK(this, ODbaseIndexDialog, RemoveAllClickHdl) );
107 	aPB_OK.SetClickHdl( LINK(this, ODbaseIndexDialog, OKClickHdl) );
108 
109 	aLB_FreeIndexes.SetSelectHdl( LINK(this, ODbaseIndexDialog, OnListEntrySelected) );
110 	aLB_TableIndexes.SetSelectHdl( LINK(this, ODbaseIndexDialog, OnListEntrySelected) );
111 
112 	aCB_Tables.SetDropDownLineCount(8);
113 	Init();
114 	SetCtrls();
115 	FreeResource();
116 
117 	// set Hi contrast bitmaps
118 	aIB_Add.SetModeImage(		ModuleRes(IMG_ONE_LEFT_H),BMP_COLOR_HIGHCONTRAST);
119 	aIB_AddAll.SetModeImage(	ModuleRes(IMG_ALL_LEFT_H),BMP_COLOR_HIGHCONTRAST);
120 	aIB_Remove.SetModeImage(	ModuleRes(IMG_ONE_RIGHT_H),BMP_COLOR_HIGHCONTRAST);
121 	aIB_RemoveAll.SetModeImage(	ModuleRes(IMG_ALL_RIGHT_H),BMP_COLOR_HIGHCONTRAST);
122 }
123 
124 //-------------------------------------------------------------------------
125 ODbaseIndexDialog::~ODbaseIndexDialog()
126 {
127 
128     DBG_DTOR(ODbaseIndexDialog,NULL);
129 }
130 
131 //-------------------------------------------------------------------------
132 sal_Bool ODbaseIndexDialog::GetTable(const String& _rName, TableInfoListIterator& _rPosition)
133 {
134 	for (	_rPosition = m_aTableInfoList.begin();
135 			_rPosition != m_aTableInfoList.end();
136 			++_rPosition
137 		)
138 	{
139 		if (m_bCaseSensitiv)
140 		{
141 			if (_rPosition->aTableName.Equals(_rName))
142 				return sal_True;
143 		}
144 		else
145 		{
146 			if (_rPosition->aTableName.EqualsIgnoreCaseAscii(_rName))
147 				return sal_True;
148 		}
149 	}
150 	return sal_False;
151 }
152 
153 //-------------------------------------------------------------------------
154 void ODbaseIndexDialog::checkButtons()
155 {
156     aIB_Add.Enable(0 != aLB_FreeIndexes.GetSelectEntryCount());
157     aIB_AddAll.Enable(0 != aLB_FreeIndexes.GetEntryCount());
158 
159     aIB_Remove.Enable(0 != aLB_TableIndexes.GetSelectEntryCount());
160     aIB_RemoveAll.Enable(0 != aLB_TableIndexes.GetEntryCount());
161 }
162 
163 //-------------------------------------------------------------------------
164 OTableIndex ODbaseIndexDialog::implRemoveIndex(const String& _rName, TableIndexList& _rList, ListBox& _rDisplay, sal_Bool _bMustExist)
165 {
166 	OTableIndex aReturn;
167 
168 	sal_Int32 nPos = 0;
169 
170 	TableIndexListIterator aSearch;
171     for (	aSearch = _rList.begin();
172 			aSearch != _rList.end();
173 			++aSearch, ++nPos
174 		)
175 	{
176 		if ( m_bCaseSensitiv ? aSearch->GetIndexFileName().Equals(_rName) : aSearch->GetIndexFileName().EqualsIgnoreCaseAscii(_rName) )
177 		{
178 			aReturn = *aSearch;
179 
180 			_rList.erase(aSearch);
181 			_rDisplay.RemoveEntry( _rName );
182 
183 			// adjust selection if necessary
184 			if ((sal_uInt32)nPos == _rList.size())
185 				_rDisplay.SelectEntryPos((sal_uInt16)nPos-1);
186 			else
187 				_rDisplay.SelectEntryPos((sal_uInt16)nPos);
188 
189 			break;
190 		}
191 	}
192 
193     (void)_bMustExist;
194 	DBG_ASSERT(!_bMustExist || (aSearch != _rList.end()), "ODbaseIndexDialog::implRemoveIndex : did not find the index!");
195 	return aReturn;
196 }
197 
198 //-------------------------------------------------------------------------
199 void ODbaseIndexDialog::implInsertIndex(const OTableIndex& _rIndex, TableIndexList& _rList, ListBox& _rDisplay)
200 {
201 	_rList.push_front( _rIndex );
202 	_rDisplay.InsertEntry( _rIndex.GetIndexFileName() );
203 	_rDisplay.SelectEntryPos(0);
204 }
205 
206 //-------------------------------------------------------------------------
207 OTableIndex ODbaseIndexDialog::RemoveTableIndex( const String& _rTableName, const String& _rIndexName, sal_Bool _bMustExist )
208 {
209 	OTableIndex aReturn;
210 
211 	// does the table exist ?
212 	TableInfoListIterator aTablePos;
213 	if (!GetTable(_rTableName, aTablePos))
214 		return aReturn;
215 
216 	return implRemoveIndex(_rIndexName, aTablePos->aIndexList, aLB_TableIndexes, _bMustExist);
217 }
218 
219 //-------------------------------------------------------------------------
220 void ODbaseIndexDialog::InsertTableIndex( const String& _rTableName, const OTableIndex& _rIndex)
221 {
222 	TableInfoListIterator aTablePos;
223 	if (!GetTable(_rTableName, aTablePos))
224 		return;
225 
226 	implInsertIndex(_rIndex, aTablePos->aIndexList, aLB_TableIndexes);
227 }
228 
229 //-------------------------------------------------------------------------
230 IMPL_LINK( ODbaseIndexDialog, OKClickHdl, PushButton*, /*pButton*/ )
231 {
232 	// let all tables write their INF file
233 
234 	for	(	ConstTableInfoListIterator aLoop = m_aTableInfoList.begin();
235 			aLoop != m_aTableInfoList.end();
236 			++aLoop
237 		)
238 		aLoop->WriteInfFile(m_aDSN);
239 
240 	EndDialog();
241 	return 0;
242 }
243 
244 //-------------------------------------------------------------------------
245 IMPL_LINK( ODbaseIndexDialog, AddClickHdl, PushButton*, /*pButton*/ )
246 {
247 	String aSelection = aLB_FreeIndexes.GetSelectEntry();
248 	String aTableName = aCB_Tables.GetText();
249 	OTableIndex aIndex = RemoveFreeIndex( aSelection, sal_True );
250 	InsertTableIndex( aTableName, aIndex );
251 
252 	checkButtons();
253 	return 0;
254 }
255 
256 //-------------------------------------------------------------------------
257 IMPL_LINK( ODbaseIndexDialog, RemoveClickHdl, PushButton*, /*pButton*/ )
258 {
259 	String aSelection = aLB_TableIndexes.GetSelectEntry();
260 	String aTableName = aCB_Tables.GetText();
261 	OTableIndex aIndex = RemoveTableIndex( aTableName, aSelection, sal_True );
262 	InsertFreeIndex( aIndex );
263 
264 	checkButtons();
265 	return 0;
266 }
267 
268 //-------------------------------------------------------------------------
269 IMPL_LINK( ODbaseIndexDialog, AddAllClickHdl, PushButton*, /*pButton*/ )
270 {
271 	sal_uInt16 nCnt = aLB_FreeIndexes.GetEntryCount();
272 	String aTableName = aCB_Tables.GetText();
273 	String aEntry;
274 
275 	for( sal_uInt16 nPos = 0; nPos < nCnt; ++nPos )
276 		InsertTableIndex( aTableName, RemoveFreeIndex( aLB_FreeIndexes.GetEntry(0), sal_True ) );
277 
278 	checkButtons();
279 	return 0;
280 }
281 
282 //-------------------------------------------------------------------------
283 IMPL_LINK( ODbaseIndexDialog, RemoveAllClickHdl, PushButton*, /*pButton*/ )
284 {
285 	sal_uInt16 nCnt = aLB_TableIndexes.GetEntryCount();
286 	String aTableName = aCB_Tables.GetText();
287 	String aEntry;
288 
289 	for( sal_uInt16 nPos = 0; nPos < nCnt; ++nPos )
290 		InsertFreeIndex( RemoveTableIndex( aTableName, aLB_TableIndexes.GetEntry(0), sal_True ) );
291 
292 	checkButtons();
293 	return 0;
294 }
295 
296 //-------------------------------------------------------------------------
297 IMPL_LINK( ODbaseIndexDialog, OnListEntrySelected, ListBox*, /*NOTINTERESTEDIN*/ )
298 {
299 	checkButtons();
300 	return 0;
301 }
302 
303 //-------------------------------------------------------------------------
304 IMPL_LINK( ODbaseIndexDialog, TableSelectHdl, ComboBox*, pComboBox )
305 {
306 	// search the table
307 	TableInfoListIterator aTablePos;
308 	if (!GetTable(pComboBox->GetText(), aTablePos))
309 		return 0L;
310 
311 	// fill the listbox for the indexes
312 	aLB_TableIndexes.Clear();
313 	for (	ConstTableIndexListIterator aLoop = aTablePos->aIndexList.begin();
314 			aLoop != aTablePos->aIndexList.end();
315 			++aLoop
316 		)
317 		aLB_TableIndexes.InsertEntry( aLoop->GetIndexFileName() );
318 
319 	if ( aTablePos->aIndexList.size() )
320 		aLB_TableIndexes.SelectEntryPos(0);
321 
322 	checkButtons();
323 	return 0;
324 }
325 
326 //-------------------------------------------------------------------------
327 void ODbaseIndexDialog::Init()
328 {
329 	aPB_OK.Disable();
330     m_FL_Indexes.Disable();
331 	m_FT_TableIndexes.Disable();
332 	aLB_TableIndexes.Disable();
333 	m_FT_AllIndexes.Disable();
334 	aLB_FreeIndexes.Disable();
335     aIB_Add.Disable();
336     aIB_Remove.Disable();
337     aIB_AddAll.Disable();
338     aIB_RemoveAll.Disable();
339 
340 	///////////////////////////////////////////////////////////////////////////
341 	// Alle Indizes werden erst einmal zur Liste der freien Indizes hinzugefuegt.
342 	// Dann wird fuer jede Tabelle in der Inf-Datei nachgeschaut, welche Indizes sie besitzt.
343 	// Diese Indizes werden aus der Liste der freien Indizes entfernt
344 	// und in die Indexliste der Tabelle eingetragen
345 
346 	///////////////////////////////////////////////////////////////////////////
347 	// if the string does not contain a path, cut the string
348 	INetURLObject aURL;
349 	aURL.SetSmartProtocol(INET_PROT_FILE);
350 	{
351 		SvtPathOptions aPathOptions;
352 		m_aDSN = aPathOptions.SubstituteVariable(m_aDSN);
353 	}
354 	aURL.SetSmartURL(m_aDSN);
355 
356 
357 	//	String aFileName = aURL.PathToFileName();
358 	m_aDSN = aURL.GetMainURL(INetURLObject::NO_DECODE);
359 	::ucbhelper::Content aFile;
360 	sal_Bool bFolder=sal_True;
361 	try
362 	{
363 		aFile = ::ucbhelper::Content(m_aDSN,Reference< ::com::sun::star::ucb::XCommandEnvironment >());
364 		bFolder = aFile.isFolder();
365 	}
366 	catch(Exception&)
367 	{
368 		return;
369 	}
370 
371 	///////////////////////////////////////////////////////////////////////////
372 	// first assume for all indexes they're free
373 
374 	Sequence< ::rtl::OUString> aFolderContent( ::utl::LocalFileHelper::GetFolderContents(m_aDSN,bFolder));
375 
376 	::rtl::OUString aIndexExt = ::rtl::OUString::createFromAscii("ndx");
377 	::rtl::OUString aTableExt = ::rtl::OUString::createFromAscii("dbf");
378 
379 	::std::vector< String > aUsedIndexes;
380 
381 	String aExt;
382 	const ::rtl::OUString *pBegin = aFolderContent.getConstArray();
383 	const ::rtl::OUString *pEnd   = pBegin + aFolderContent.getLength();
384 	aURL.SetSmartProtocol(INET_PROT_FILE);
385 	for(;pBegin != pEnd;++pBegin)
386 	{
387 		String aName;
388 		::utl::LocalFileHelper::ConvertURLToPhysicalName(pBegin->getStr(),aName);
389 		aURL.SetSmartURL(aName);
390 		aExt = aURL.getExtension();
391 		if(aExt == aIndexExt.getStr())
392 		{
393 			m_aFreeIndexList.push_back( OTableIndex(aURL.getName()) );
394 		}
395 		else if(aExt == aTableExt.getStr())
396 		{
397 			m_aTableInfoList.push_back( OTableInfo(aURL.getName()) );
398 			OTableInfo& rTabInfo = m_aTableInfoList.back();
399 
400 			// open the INF file
401 			aURL.setExtension(String::CreateFromAscii("inf"));
402 			OFileNotation aTransformer(aURL.GetURLNoPass(), OFileNotation::N_URL);
403 			Config aInfFile( aTransformer.get(OFileNotation::N_SYSTEM) );
404 			aInfFile.SetGroup( aGroupIdent );
405 
406 			///////////////////////////////////////////////////////////////////////////
407 			// fill the indexes list
408 			ByteString aNDX;
409 			sal_uInt16 nKeyCnt = aInfFile.GetKeyCount();
410 			ByteString aKeyName;
411 			String aEntry;
412 
413 			for( sal_uInt16 nKey = 0; nKey < nKeyCnt; nKey++ )
414 			{
415 				// does the key point to an index file ?
416 				aKeyName = aInfFile.GetKeyName( nKey );
417 				aNDX = aKeyName.Copy(0,3);
418 
419 				// yes -> add to the tables index list
420 				if (aNDX == "NDX" )
421 				{
422 					aEntry = String(aInfFile.ReadKey(aKeyName), gsl_getSystemTextEncoding());
423 					rTabInfo.aIndexList.push_back( OTableIndex( aEntry ) );
424 
425 					// and remove it from the free index list
426 					aUsedIndexes.push_back(aEntry);
427 						// do this later below. We may not have encountered the index file, yet, thus we may not
428 						// know the index as beeing free, yet
429 				}
430 
431 			}
432 		}
433 	}
434 
435 	for	(	::std::vector< String >::const_iterator aUsedIndex = aUsedIndexes.begin();
436 			aUsedIndex != aUsedIndexes.end();
437 			++aUsedIndex
438 		)
439 		RemoveFreeIndex( *aUsedIndex, sal_False );
440 
441 	if (m_aTableInfoList.size())
442 	{
443 		aPB_OK.Enable();
444         m_FL_Indexes.Enable();
445 		m_FT_TableIndexes.Enable();
446 		aLB_TableIndexes.Enable();
447 		m_FT_AllIndexes.Enable();
448 		aLB_FreeIndexes.Enable();
449 	}
450 
451 	checkButtons();
452 }
453 
454 //-------------------------------------------------------------------------
455 void ODbaseIndexDialog::SetCtrls()
456 {
457 	// ComboBox Tabellen
458 	for	(	ConstTableInfoListIterator aLoop = m_aTableInfoList.begin();
459 			aLoop != m_aTableInfoList.end();
460 			++aLoop
461 		)
462 		aCB_Tables.InsertEntry( aLoop->aTableName );
463 
464 	// Den ersten Datensatz ins Edit stellen
465 	if( m_aTableInfoList.size() )
466 	{
467 		const OTableInfo& rTabInfo = m_aTableInfoList.front();
468 		aCB_Tables.SetText( rTabInfo.aTableName );
469 
470 		// ListBox der Tabellenindizes aufbauen
471 		for	(	ConstTableIndexListIterator aIndex = rTabInfo.aIndexList.begin();
472 				aIndex != rTabInfo.aIndexList.end();
473 				++aIndex
474 			)
475 			aLB_TableIndexes.InsertEntry( aIndex->GetIndexFileName() );
476 
477 		if( rTabInfo.aIndexList.size() )
478 			aLB_TableIndexes.SelectEntryPos( 0 );
479 
480 	}
481 
482 	// ListBox freie Indizes
483 	for	(	ConstTableIndexListIterator aFree = m_aFreeIndexList.begin();
484 			aFree != m_aFreeIndexList.end();
485 			++aFree
486 		)
487 		aLB_FreeIndexes.InsertEntry( aFree->GetIndexFileName() );
488 
489 	if( m_aFreeIndexList.size() )
490 		aLB_FreeIndexes.SelectEntryPos( 0 );
491 
492 
493 	TableSelectHdl(&aCB_Tables);
494 	checkButtons();
495 }
496 
497 //////////////////////////////////////////////////////////////////////////
498 // Klasse OTableInfo
499 //-------------------------------------------------------------------------
500 void OTableInfo::WriteInfFile( const String& rDSN ) const
501 {
502 	// INF-Datei oeffnen
503 	INetURLObject aURL;
504 	aURL.SetSmartProtocol(INET_PROT_FILE);
505 	String aDsn = rDSN;
506 	{
507 		SvtPathOptions aPathOptions;
508 		aDsn = aPathOptions.SubstituteVariable(aDsn);
509 	}
510 	aURL.SetSmartURL(aDsn);
511 	aURL.Append(aTableName);
512 	aURL.setExtension(String::CreateFromAscii("inf"));
513 
514 	OFileNotation aTransformer(aURL.GetURLNoPass(), OFileNotation::N_URL);
515 	Config aInfFile( aTransformer.get(OFileNotation::N_SYSTEM) );
516 	aInfFile.SetGroup( aGroupIdent );
517 
518 	// Erst einmal alle Tabellenindizes loeschen
519 	ByteString aNDX;
520 	sal_uInt16 nKeyCnt = aInfFile.GetKeyCount();
521 	ByteString aKeyName;
522 	ByteString aEntry;
523 	sal_uInt16 nKey = 0;
524 
525 	while( nKey < nKeyCnt )
526 	{
527 		// Verweist der Key auf ein Indexfile?...
528 		aKeyName = aInfFile.GetKeyName( nKey );
529 		aNDX = aKeyName.Copy(0,3);
530 
531 		//...wenn ja, Indexfile loeschen, nKey steht dann auf nachfolgendem Key
532 		if( aNDX == "NDX" )
533 		{
534 			aInfFile.DeleteKey(aKeyName);
535 			nKeyCnt--;
536 		}
537 		else
538 			nKey++;
539 
540 	}
541 
542 	// Jetzt alle gespeicherten Indizes hinzufuegen
543 	sal_uInt16 nPos = 0;
544 	for	(	ConstTableIndexListIterator aIndex = aIndexList.begin();
545 			aIndex != aIndexList.end();
546 			++aIndex, ++nPos
547 		)
548 	{
549 		aKeyName = "NDX";
550 		if( nPos > 0 )	// Erster Index erhaelt keine Ziffer
551 			aKeyName += ByteString::CreateFromInt32( nPos );
552 		aInfFile.WriteKey( aKeyName, ByteString(aIndex->GetIndexFileName(), gsl_getSystemTextEncoding()) );
553 	}
554 
555 	aInfFile.Flush();
556 
557 	// Falls nur noch [dbase] in INF-File steht, Datei loeschen
558 	if(!nPos)
559 	{
560         try
561         {
562 		    ::ucbhelper::Content aContent(aURL.GetURLNoPass(),Reference<XCommandEnvironment>());
563 		    aContent.executeCommand( rtl::OUString::createFromAscii( "delete" ),makeAny( sal_Bool( sal_True ) ) );
564         }
565         catch (const Exception& e )
566         {
567             (void)e;  // make compiler happy
568             // simply silent this. The strange algorithm here does a lot of things even if no files at all were
569             // created or accessed, so it's possible that the file we're trying to delete does not even exist,
570             // and this is a valid condition.
571             // 2003-05-15 - #109677# - fs@openoffice.org
572         }
573 	}
574 }
575 
576 //.........................................................................
577 }	// namespace dbaui
578 //.........................................................................
579 
580