1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <memory>
23 
24 #include "advancedsettings.hxx"
25 #include <advancedsettingsdlg.hxx>
26 #include <dsitems.hxx>
27 #include "DbAdminImpl.hxx"
28 #include "DriverSettings.hxx"
29 #include "optionalboolitem.hxx"
30 #include <dbu_dlg.hxx>
31 
32 #include <svl/eitem.hxx>
33 #include <svl/intitem.hxx>
34 #include <svl/stritem.hxx>
35 
36 namespace dbaui
37 {
38 
39     using ::com::sun::star::uno::Reference;
40     using ::com::sun::star::uno::Any;
41     using ::com::sun::star::uno::XComponentContext;
42     using ::com::sun::star::beans::XPropertySet;
43     using ::com::sun::star::sdbc::XConnection;
44     using ::com::sun::star::sdbc::XDriver;
45 
46     // SpecialSettingsPage
47     struct BooleanSettingDesc
48     {
49         std::unique_ptr<weld::CheckButton>& xControl; // the dialog's control which displays this setting
50         OString     sControlId;         // the widget name of the control in the .ui
51         sal_uInt16  nItemId;            // the ID of the item (in an SfxItemSet) which corresponds to this setting
52         bool        bInvertedDisplay;   // true if and only if the checkbox is checked when the item is sal_False, and vice versa
53         bool        bOptionalBool;      // type is OptionalBool
54     };
55 
56     // SpecialSettingsPage
SpecialSettingsPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & _rCoreAttrs,const DataSourceMetaData & _rDSMeta)57     SpecialSettingsPage::SpecialSettingsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& _rCoreAttrs, const DataSourceMetaData& _rDSMeta)
58         : OGenericAdministrationPage(pPage, pController, "dbaccess/ui/specialsettingspage.ui", "SpecialSettingsPage", _rCoreAttrs)
59         , m_aBooleanSettings {
60             { m_xIsSQL92Check,               "usesql92",        DSID_SQL92CHECK,            false, false },
61             { m_xAppendTableAlias,           "append",          DSID_APPEND_TABLE_ALIAS,    false, false },
62             { m_xAsBeforeCorrelationName,    "useas",           DSID_AS_BEFORE_CORRNAME,    false, false },
63             { m_xEnableOuterJoin,            "useoj",           DSID_ENABLEOUTERJOIN,       false, false },
64             { m_xIgnoreDriverPrivileges,     "ignoreprivs",     DSID_IGNOREDRIVER_PRIV,     false, false },
65             { m_xParameterSubstitution,      "replaceparams",   DSID_PARAMETERNAMESUBST,    false, false },
66             { m_xSuppressVersionColumn,      "displayver",      DSID_SUPPRESSVERSIONCL,     true,  false },
67             { m_xCatalog,                    "usecatalogname",  DSID_CATALOG,               false, false },
68             { m_xSchema,                     "useschemaname",   DSID_SCHEMA,                false, false },
69             { m_xIndexAppendix,              "createindex",     DSID_INDEXAPPENDIX,         false, false },
70             { m_xDosLineEnds,                "eol",             DSID_DOSLINEENDS,           false, false },
71             { m_xCheckRequiredFields,        "inputchecks",     DSID_CHECK_REQUIRED_FIELDS, false, false },
72             { m_xIgnoreCurrency,             "ignorecurrency",  DSID_IGNORECURRENCY,        false, false },
73             { m_xEscapeDateTime,             "useodbcliterals", DSID_ESCAPE_DATETIME,       false, false },
74             { m_xPrimaryKeySupport,          "primarykeys",     DSID_PRIMARY_KEY_SUPPORT,   false, false },
75             { m_xRespectDriverResultSetType, "resulttype",      DSID_RESPECTRESULTSETTYPE,  false, false } }
76         , m_bHasBooleanComparisonMode( _rDSMeta.getFeatureSet().has( DSID_BOOLEANCOMPARISON ) )
77         , m_bHasMaxRowScan( _rDSMeta.getFeatureSet().has( DSID_MAX_ROW_SCAN ) )
78     {
79         const FeatureSet& rFeatures( _rDSMeta.getFeatureSet() );
80         // create all the check boxes for the boolean settings
81         for (auto & booleanSetting : m_aBooleanSettings)
82         {
83             sal_uInt16 nItemId = booleanSetting.nItemId;
84             if ( rFeatures.has( nItemId ) )
85             {
86                 // check whether this must be a tristate check box
87                 const SfxPoolItem& rItem = _rCoreAttrs.Get(nItemId);
88                 booleanSetting.bOptionalBool = dynamic_cast<const OptionalBoolItem*>(&rItem) != nullptr;
89                 booleanSetting.xControl = m_xBuilder->weld_check_button(booleanSetting.sControlId);
90                 if (booleanSetting.bOptionalBool)
91                     booleanSetting.xControl->connect_toggled(LINK(this, SpecialSettingsPage, OnTriStateToggleHdl));
92                 else
93                     booleanSetting.xControl->connect_toggled(LINK(this, SpecialSettingsPage, OnToggleHdl));
94                 booleanSetting.xControl->show();
95             }
96         }
97 
98         // create the controls for the boolean comparison mode
99         if ( m_bHasBooleanComparisonMode )
100         {
101             m_xBooleanComparisonModeLabel = m_xBuilder->weld_label("comparisonft");
102             m_xBooleanComparisonMode = m_xBuilder->weld_combo_box("comparison");
103             m_xBooleanComparisonMode->connect_changed(LINK(this, SpecialSettingsPage, BooleanComparisonSelectHdl));
104             m_xBooleanComparisonModeLabel->show();
105             m_xBooleanComparisonMode->show();
106         }
107         // create the controls for the max row scan
108         if ( m_bHasMaxRowScan )
109         {
110             m_xMaxRowScanLabel  = m_xBuilder->weld_label("rowsft");
111             m_xMaxRowScan = m_xBuilder->weld_spin_button("rows");
112             m_xMaxRowScan->connect_value_changed(LINK(this, OGenericAdministrationPage, OnControlSpinButtonModifyHdl));
113             m_xMaxRowScanLabel->show();
114             m_xMaxRowScan->show();
115         }
116     }
117 
IMPL_LINK(SpecialSettingsPage,OnTriStateToggleHdl,weld::ToggleButton &,rToggle,void)118     IMPL_LINK(SpecialSettingsPage, OnTriStateToggleHdl, weld::ToggleButton&, rToggle, void)
119     {
120         auto eOldState = m_aTriStates[&rToggle];
121         switch (eOldState)
122         {
123             case TRISTATE_INDET:
124                 rToggle.set_state(TRISTATE_FALSE);
125                 break;
126             case TRISTATE_TRUE:
127                 rToggle.set_state(TRISTATE_INDET);
128                 break;
129             case TRISTATE_FALSE:
130                 rToggle.set_state(TRISTATE_TRUE);
131                 break;
132         }
133         m_aTriStates[&rToggle] = rToggle.get_state();
134         OnToggleHdl(rToggle);
135     }
136 
IMPL_LINK(SpecialSettingsPage,OnToggleHdl,weld::ToggleButton &,rBtn,void)137     IMPL_LINK(SpecialSettingsPage, OnToggleHdl, weld::ToggleButton&, rBtn, void)
138     {
139         if (&rBtn == m_xAppendTableAlias.get() && m_xAsBeforeCorrelationName)
140         {
141             // make m_xAsBeforeCorrelationName depend on m_xAppendTableAlias
142             m_xAsBeforeCorrelationName->set_sensitive(m_xAppendTableAlias->get_active());
143         }
144         OnControlModifiedButtonClick(rBtn);
145     }
146 
IMPL_LINK(SpecialSettingsPage,BooleanComparisonSelectHdl,weld::ComboBox &,rControl,void)147     IMPL_LINK(SpecialSettingsPage, BooleanComparisonSelectHdl, weld::ComboBox&, rControl, void)
148     {
149         callModifiedHdl(&rControl);
150     }
151 
~SpecialSettingsPage()152     SpecialSettingsPage::~SpecialSettingsPage()
153     {
154     }
155 
fillWindows(std::vector<std::unique_ptr<ISaveValueWrapper>> & _rControlList)156     void SpecialSettingsPage::fillWindows( std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList )
157     {
158         if ( m_bHasBooleanComparisonMode )
159         {
160             _rControlList.emplace_back(new ODisableWidgetWrapper<weld::Label>(m_xBooleanComparisonModeLabel.get()));
161         }
162         if ( m_bHasMaxRowScan )
163         {
164             _rControlList.emplace_back(new ODisableWidgetWrapper<weld::Label>(m_xMaxRowScanLabel.get()));
165         }
166     }
167 
fillControls(std::vector<std::unique_ptr<ISaveValueWrapper>> & _rControlList)168     void SpecialSettingsPage::fillControls(std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList)
169     {
170         for (auto const& booleanSetting : m_aBooleanSettings)
171         {
172             if (booleanSetting.xControl)
173             {
174                 _rControlList.emplace_back(new OSaveValueWidgetWrapper<weld::ToggleButton>(booleanSetting.xControl.get()));
175             }
176         }
177 
178         if ( m_bHasBooleanComparisonMode )
179             _rControlList.emplace_back(new OSaveValueWidgetWrapper<weld::ComboBox>(m_xBooleanComparisonMode.get()));
180         if ( m_bHasMaxRowScan )
181             _rControlList.emplace_back(new OSaveValueWidgetWrapper<weld::SpinButton>(m_xMaxRowScan.get()));
182     }
183 
implInitControls(const SfxItemSet & _rSet,bool _bSaveValue)184     void SpecialSettingsPage::implInitControls(const SfxItemSet& _rSet, bool _bSaveValue)
185     {
186         // check whether or not the selection is invalid or readonly (invalid implies readonly, but not vice versa)
187         bool bValid, bReadonly;
188         getFlags( _rSet, bValid, bReadonly );
189 
190         if ( !bValid )
191         {
192             OGenericAdministrationPage::implInitControls(_rSet, _bSaveValue);
193             return;
194         }
195 
196         m_aTriStates.clear();
197 
198         // the boolean items
199         for (auto const& booleanSetting : m_aBooleanSettings)
200         {
201             if (!booleanSetting.xControl)
202                 continue;
203 
204             bool bTriState = false;
205 
206             boost::optional<bool> aValue;
207 
208             const SfxPoolItem* pItem = _rSet.GetItem<SfxPoolItem>(booleanSetting.nItemId);
209             if (const SfxBoolItem *pBoolItem = dynamic_cast<const SfxBoolItem*>( pItem) )
210             {
211                 aValue = pBoolItem->GetValue();
212             }
213             else if (const OptionalBoolItem *pOptionalItem = dynamic_cast<const OptionalBoolItem*>( pItem) )
214             {
215                 aValue = pOptionalItem->GetFullValue();
216                 bTriState = true;
217             }
218             else
219                 OSL_FAIL( "SpecialSettingsPage::implInitControls: unknown boolean item type!" );
220 
221             if ( !aValue )
222             {
223                 booleanSetting.xControl->set_state(TRISTATE_INDET);
224             }
225             else
226             {
227                 bool bValue = *aValue;
228                 if ( booleanSetting.bInvertedDisplay )
229                     bValue = !bValue;
230                 booleanSetting.xControl->set_active(bValue);
231             }
232             if (bTriState)
233                 m_aTriStates[booleanSetting.xControl.get()] = booleanSetting.xControl->get_state();
234         }
235 
236         if (m_xAppendTableAlias && m_xAsBeforeCorrelationName)
237         {
238             // make m_xAsBeforeCorrelationName depend on m_xAppendTableAlias
239             m_xAsBeforeCorrelationName->set_sensitive(m_xAppendTableAlias->get_active());
240         }
241 
242         // the non-boolean items
243         if ( m_bHasBooleanComparisonMode )
244         {
245             const SfxInt32Item* pBooleanComparison = _rSet.GetItem<SfxInt32Item>(DSID_BOOLEANCOMPARISON);
246             m_xBooleanComparisonMode->set_active(static_cast<sal_uInt16>(pBooleanComparison->GetValue()));
247         }
248 
249         if ( m_bHasMaxRowScan )
250         {
251             const SfxInt32Item* pMaxRowScan = _rSet.GetItem<SfxInt32Item>(DSID_MAX_ROW_SCAN);
252             m_xMaxRowScan->set_value(pMaxRowScan->GetValue());
253         }
254 
255         OGenericAdministrationPage::implInitControls(_rSet, _bSaveValue);
256     }
257 
FillItemSet(SfxItemSet * _rSet)258     bool SpecialSettingsPage::FillItemSet( SfxItemSet* _rSet )
259     {
260         bool bChangedSomething = false;
261 
262         // the boolean items
263         for (auto const& booleanSetting : m_aBooleanSettings)
264         {
265             if (!booleanSetting.xControl)
266                 continue;
267             fillBool(*_rSet, booleanSetting.xControl.get(), booleanSetting.nItemId, booleanSetting.bOptionalBool, bChangedSomething, booleanSetting.bInvertedDisplay);
268         }
269 
270         // the non-boolean items
271         if ( m_bHasBooleanComparisonMode )
272         {
273             if (m_xBooleanComparisonMode->get_value_changed_from_saved())
274             {
275                 _rSet->Put(SfxInt32Item(DSID_BOOLEANCOMPARISON, m_xBooleanComparisonMode->get_active()));
276                 bChangedSomething = true;
277             }
278         }
279         if ( m_bHasMaxRowScan )
280         {
281             fillInt32(*_rSet,m_xMaxRowScan.get(),DSID_MAX_ROW_SCAN,bChangedSomething);
282         }
283         return bChangedSomething;
284     }
285 
286     // GeneratedValuesPage
GeneratedValuesPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & _rCoreAttrs)287     GeneratedValuesPage::GeneratedValuesPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& _rCoreAttrs)
288         : OGenericAdministrationPage(pPage, pController, "dbaccess/ui/generatedvaluespage.ui", "GeneratedValuesPage", _rCoreAttrs)
289         , m_xAutoRetrievingEnabled(m_xBuilder->weld_check_button("autoretrieve"))
290         , m_xGrid(m_xBuilder->weld_widget("grid"))
291         , m_xAutoIncrementLabel(m_xBuilder->weld_label("statementft"))
292         , m_xAutoIncrement(m_xBuilder->weld_entry("statement"))
293         , m_xAutoRetrievingLabel(m_xBuilder->weld_label("queryft"))
294         , m_xAutoRetrieving(m_xBuilder->weld_entry("query"))
295     {
296         m_xAutoRetrievingEnabled->connect_toggled(LINK(this, GeneratedValuesPage, OnAutoToggleHdl));
297         m_xAutoIncrement->connect_changed(LINK(this, OGenericAdministrationPage, OnControlEntryModifyHdl));
298         m_xAutoRetrieving->connect_changed(LINK(this, OGenericAdministrationPage, OnControlEntryModifyHdl));
299     }
300 
IMPL_LINK(GeneratedValuesPage,OnAutoToggleHdl,weld::ToggleButton &,rBtn,void)301     IMPL_LINK(GeneratedValuesPage, OnAutoToggleHdl, weld::ToggleButton&, rBtn, void)
302     {
303         m_xGrid->set_sensitive(rBtn.get_active());
304         OnControlModifiedButtonClick(rBtn);
305     }
306 
~GeneratedValuesPage()307     GeneratedValuesPage::~GeneratedValuesPage()
308     {
309     }
310 
fillWindows(std::vector<std::unique_ptr<ISaveValueWrapper>> & _rControlList)311     void GeneratedValuesPage::fillWindows( std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList )
312     {
313         _rControlList.emplace_back(new ODisableWidgetWrapper<weld::Widget>(m_xContainer.get()));
314     }
315 
fillControls(std::vector<std::unique_ptr<ISaveValueWrapper>> & _rControlList)316     void GeneratedValuesPage::fillControls( std::vector< std::unique_ptr<ISaveValueWrapper> >& _rControlList )
317     {
318         _rControlList.emplace_back( new OSaveValueWidgetWrapper<weld::ToggleButton>( m_xAutoRetrievingEnabled.get() ) );
319         _rControlList.emplace_back( new OSaveValueWidgetWrapper<weld::Entry>( m_xAutoIncrement.get() ) );
320         _rControlList.emplace_back( new OSaveValueWidgetWrapper<weld::Entry>( m_xAutoRetrieving.get() ) );
321     }
322 
implInitControls(const SfxItemSet & _rSet,bool _bSaveValue)323     void GeneratedValuesPage::implInitControls( const SfxItemSet& _rSet, bool _bSaveValue )
324     {
325         // check whether or not the selection is invalid or readonly (invalid implies readonly, but not vice versa)
326         bool bValid, bReadonly;
327         getFlags(_rSet, bValid, bReadonly);
328 
329         // collect the items
330         const SfxStringItem* pAutoIncrementItem = _rSet.GetItem<SfxStringItem>(DSID_AUTOINCREMENTVALUE);
331         const SfxStringItem* pAutoRetrieveValueItem = _rSet.GetItem<SfxStringItem>(DSID_AUTORETRIEVEVALUE);
332         const SfxBoolItem* pAutoRetrieveEnabledItem = _rSet.GetItem<SfxBoolItem>(DSID_AUTORETRIEVEENABLED);
333 
334         // forward the values to the controls
335         if (bValid)
336         {
337             bool bEnabled = pAutoRetrieveEnabledItem->GetValue();
338             m_xAutoRetrievingEnabled->set_active(bEnabled);
339 
340             m_xAutoIncrement->set_text(pAutoIncrementItem->GetValue());
341             m_xAutoIncrement->save_value();
342             m_xAutoRetrieving->set_text(pAutoRetrieveValueItem->GetValue());
343             m_xAutoRetrieving->save_value();
344         }
345         OGenericAdministrationPage::implInitControls( _rSet, _bSaveValue );
346     }
347 
FillItemSet(SfxItemSet * _rSet)348     bool GeneratedValuesPage::FillItemSet(SfxItemSet* _rSet)
349     {
350         bool bChangedSomething = false;
351 
352         fillString( *_rSet, m_xAutoIncrement.get(), DSID_AUTOINCREMENTVALUE, bChangedSomething );
353         fillBool( *_rSet, m_xAutoRetrievingEnabled.get(), DSID_AUTORETRIEVEENABLED, false, bChangedSomething );
354         fillString( *_rSet, m_xAutoRetrieving.get(), DSID_AUTORETRIEVEVALUE, bChangedSomething );
355 
356         return bChangedSomething;
357     }
358 
359     // AdvancedSettingsDialog
AdvancedSettingsDialog(weld::Window * pParent,SfxItemSet * _pItems,const Reference<XComponentContext> & _rxContext,const Any & _aDataSourceName)360     AdvancedSettingsDialog::AdvancedSettingsDialog(weld::Window* pParent, SfxItemSet* _pItems,
361         const Reference< XComponentContext >& _rxContext, const Any& _aDataSourceName )
362         : SfxTabDialogController(pParent, "dbaccess/ui/advancedsettingsdialog.ui", "AdvancedSettingsDialog", _pItems)
363     {
364         m_pImpl.reset(new ODbDataSourceAdministrationHelper(_rxContext, m_xDialog.get(), pParent, this));
365         m_pImpl->setDataSourceOrName(_aDataSourceName);
366         Reference< XPropertySet > xDatasource = m_pImpl->getCurrentDataSource();
367         m_pImpl->translateProperties(xDatasource, *_pItems);
368         SetInputSet(_pItems);
369         // propagate this set as our new input set and reset the example set
370         m_xExampleSet.reset(new SfxItemSet(*GetInputSetImpl()));
371 
372         const OUString eType = dbaui::ODbDataSourceAdministrationHelper::getDatasourceType(*_pItems);
373 
374         DataSourceMetaData aMeta( eType );
375         const FeatureSet& rFeatures( aMeta.getFeatureSet() );
376 
377         // auto-generated values?
378         if (rFeatures.supportsGeneratedValues())
379             AddTabPage("generated", ODriversSettings::CreateGeneratedValuesPage, nullptr);
380         else
381             RemoveTabPage("generated");
382 
383         // any "special settings"?
384         if (rFeatures.supportsAnySpecialSetting())
385             AddTabPage("special", ODriversSettings::CreateSpecialSettingsPage, nullptr);
386         else
387             RemoveTabPage("special");
388 
389         // remove the reset button - it's meaning is much too ambiguous in this dialog
390         RemoveResetButton();
391     }
392 
~AdvancedSettingsDialog()393     AdvancedSettingsDialog::~AdvancedSettingsDialog()
394     {
395         SetInputSet(nullptr);
396     }
397 
doesHaveAnyAdvancedSettings(const OUString & _sURL)398     bool AdvancedSettingsDialog::doesHaveAnyAdvancedSettings( const OUString& _sURL )
399     {
400         DataSourceMetaData aMeta( _sURL );
401         const FeatureSet& rFeatures( aMeta.getFeatureSet() );
402         return rFeatures.supportsGeneratedValues() || rFeatures.supportsAnySpecialSetting();
403     }
404 
Ok()405     short AdvancedSettingsDialog::Ok()
406     {
407         short nRet = SfxTabDialogController::Ok();
408         if ( nRet == RET_OK )
409         {
410             m_xExampleSet->Put(*GetOutputItemSet());
411             m_pImpl->saveChanges(*m_xExampleSet);
412         }
413         return nRet;
414     }
415 
PageCreated(const OString & rId,SfxTabPage & _rPage)416     void AdvancedSettingsDialog::PageCreated(const OString& rId, SfxTabPage& _rPage)
417     {
418         // register ourself as modified listener
419         static_cast<OGenericAdministrationPage&>(_rPage).SetServiceFactory( getORB() );
420         static_cast<OGenericAdministrationPage&>(_rPage).SetAdminDialog(this,this);
421         SfxTabDialogController::PageCreated(rId, _rPage);
422     }
423 
getOutputSet() const424     const SfxItemSet* AdvancedSettingsDialog::getOutputSet() const
425     {
426         return m_xExampleSet.get();
427     }
428 
getWriteOutputSet()429     SfxItemSet* AdvancedSettingsDialog::getWriteOutputSet()
430     {
431         return m_xExampleSet.get();
432     }
433 
createConnection()434     std::pair< Reference< XConnection >, bool > AdvancedSettingsDialog::createConnection()
435     {
436         return m_pImpl->createConnection();
437     }
438 
getORB() const439     Reference< XComponentContext > AdvancedSettingsDialog::getORB() const
440     {
441         return m_pImpl->getORB();
442     }
443 
getDriver()444     Reference< XDriver > AdvancedSettingsDialog::getDriver()
445     {
446         return m_pImpl->getDriver();
447     }
448 
getDatasourceType(const SfxItemSet & _rSet) const449     OUString AdvancedSettingsDialog::getDatasourceType(const SfxItemSet& _rSet) const
450     {
451         return dbaui::ODbDataSourceAdministrationHelper::getDatasourceType(_rSet);
452     }
453 
clearPassword()454     void AdvancedSettingsDialog::clearPassword()
455     {
456         m_pImpl->clearPassword();
457     }
458 
setTitle(const OUString & _sTitle)459     void AdvancedSettingsDialog::setTitle(const OUString& _sTitle)
460     {
461         m_xDialog->set_title(_sTitle);
462     }
463 
enableConfirmSettings(bool)464     void AdvancedSettingsDialog::enableConfirmSettings( bool ) {}
465 
saveDatasource()466     void AdvancedSettingsDialog::saveDatasource()
467     {
468         PrepareLeaveCurrentPage();
469     }
470 
471 } // namespace dbaui
472 
473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
474