1 /***************************************************************************
2    qgshanasourceselect.cpp
3    --------------------------------------
4    Date      : 31-05-2019
5    Copyright : (C) SAP SE
6    Author    : Maxim Rylov
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  ***************************************************************************/
17 #include "qgsapplication.h"
18 #include "qgsdatasourceuri.h"
19 #include "qgsgui.h"
20 #include "qgshanasourceselect.h"
21 #include "qgshanaconnection.h"
22 #include "qgshananewconnection.h"
23 #include "qgshanaprovider.h"
24 #include "qgshanatablemodel.h"
25 #include "qgshanautils.h"
26 #include "qgslogger.h"
27 #include "qgsmanageconnectionsdialog.h"
28 #include "qgsproxyprogresstask.h"
29 #include "qgsquerybuilder.h"
30 #include "qgsvectorlayer.h"
31 #include "qgssettings.h"
32 
33 #include <QComboBox>
34 #include <QFileDialog>
35 #include <QHeaderView>
36 #include <QInputDialog>
37 #include <QMessageBox>
38 #include <QStringList>
39 #include <QStyledItemDelegate>
40 
41 //! Used to create an editor for when the user tries to change the contents of a cell
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const42 QWidget *QgsHanaSourceSelectDelegate::createEditor(
43   QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
44 {
45   Q_UNUSED( option );
46 
47   const QString tableName = index.sibling( index.row(), QgsHanaTableModel::DbtmTable ).data( Qt::DisplayRole ).toString();
48   if ( tableName.isEmpty() )
49     return nullptr;
50 
51   if ( index.column() == QgsHanaTableModel::DbtmSql )
52   {
53     return new QLineEdit( parent );
54   }
55 
56   if ( index.column() == QgsHanaTableModel::DbtmGeomType && index.data( Qt::UserRole + 1 ).toBool() )
57   {
58     QComboBox *cb = new QComboBox( parent );
59     for ( const QgsWkbTypes::Type type :
60           QList<QgsWkbTypes::Type>()
61           << QgsWkbTypes::Point
62           << QgsWkbTypes::LineString
63           << QgsWkbTypes::Polygon
64           << QgsWkbTypes::MultiPoint
65           << QgsWkbTypes::MultiLineString
66           << QgsWkbTypes::MultiPolygon
67           << QgsWkbTypes::CircularString
68           << QgsWkbTypes::GeometryCollection
69           << QgsWkbTypes::NoGeometry )
70     {
71       cb->addItem( QgsHanaTableModel::iconForWkbType( type ), QgsWkbTypes::displayString( type ), type );
72     }
73     return cb;
74   }
75 
76   if ( index.column() == QgsHanaTableModel::DbtmPkCol )
77   {
78     const QStringList values = index.data( Qt::UserRole + 1 ).toStringList();
79 
80     if ( !values.isEmpty() )
81     {
82       QComboBox *cb = new QComboBox( parent );
83       cb->setItemDelegate( new QStyledItemDelegate( parent ) );
84 
85       QStandardItemModel *model = new QStandardItemModel( values.size(), 1, cb );
86 
87       int row = 0;
88       for ( const QString &value : values )
89       {
90         QStandardItem *item = new QStandardItem( value );
91         item->setFlags( Qt::ItemIsUserCheckable | Qt::ItemIsEnabled );
92         item->setCheckable( true );
93         item->setData( Qt::Unchecked, Qt::CheckStateRole );
94         model->setItem( row++, 0, item );
95       }
96 
97       cb->setModel( model );
98 
99       return cb;
100     }
101   }
102 
103   if ( index.column() == QgsHanaTableModel::DbtmSrid )
104   {
105     QLineEdit *le = new QLineEdit( parent );
106     le->setValidator( new QIntValidator( -1, 999999, parent ) );
107     return le;
108   }
109 
110   return nullptr;
111 }
112 
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const113 void QgsHanaSourceSelectDelegate::setModelData(
114   QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
115 {
116   QComboBox *cb = qobject_cast<QComboBox *>( editor );
117   if ( cb )
118   {
119     if ( index.column() == QgsHanaTableModel::DbtmGeomType )
120     {
121       const QgsWkbTypes::Type type = static_cast<QgsWkbTypes::Type>( cb->currentData().toInt() );
122 
123       model->setData( index, QgsHanaTableModel::iconForWkbType( type ), Qt::DecorationRole );
124       model->setData( index, type != QgsWkbTypes::Unknown ? QgsWkbTypes::displayString( type ) : tr( "Select…" ) );
125       model->setData( index, type, Qt::UserRole + 2 );
126     }
127     else if ( index.column() == QgsHanaTableModel::DbtmPkCol )
128     {
129       QStandardItemModel *cbm = qobject_cast<QStandardItemModel *>( cb->model() );
130       QStringList cols;
131       for ( int idx = 0; idx < cbm->rowCount(); idx++ )
132       {
133         QStandardItem *item = cbm->item( idx, 0 );
134         if ( item->data( Qt::CheckStateRole ) == Qt::Checked )
135           cols << item->text();
136       }
137 
138       model->setData( index, cols.isEmpty() ? tr( "Select…" ) : cols.join( QLatin1String( ", " ) ) );
139       model->setData( index, cols, Qt::UserRole + 2 );
140     }
141   }
142 
143   QLineEdit *le = qobject_cast<QLineEdit *>( editor );
144   if ( le )
145   {
146     QString value( le->text() );
147 
148     if ( index.column() == QgsHanaTableModel::DbtmSrid && value.isEmpty() )
149       value = tr( "Enter…" );
150 
151     model->setData( index, value );
152   }
153 }
154 
setEditorData(QWidget * editor,const QModelIndex & index) const155 void QgsHanaSourceSelectDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
156 {
157   QString value( index.data( Qt::DisplayRole ).toString() );
158 
159   QComboBox *cb = qobject_cast<QComboBox *>( editor );
160   if ( cb )
161   {
162     if ( index.column() == QgsHanaTableModel::DbtmGeomType )
163       cb->setCurrentIndex( cb->findData( index.data( Qt::UserRole + 2 ).toInt() ) );
164 
165     if ( index.column() == QgsHanaTableModel::DbtmPkCol &&
166          !index.data( Qt::UserRole + 2 ).toStringList().isEmpty() )
167     {
168       const QStringList columns = index.data( Qt::UserRole + 2 ).toStringList();
169       for ( const QString &colName : columns )
170       {
171         QStandardItemModel *cbm = qobject_cast<QStandardItemModel *>( cb->model() );
172         for ( int idx = 0; idx < cbm->rowCount(); ++idx )
173         {
174           QStandardItem *item = cbm->item( idx, 0 );
175           if ( item->text() != colName )
176             continue;
177 
178           item->setData( Qt::Checked, Qt::CheckStateRole );
179           break;
180         }
181       }
182     }
183   }
184 
185   QLineEdit *le = qobject_cast<QLineEdit *>( editor );
186   if ( le )
187   {
188     bool ok;
189     ( void )value.toInt( &ok );
190     if ( index.column() == QgsHanaTableModel::DbtmSrid && !ok )
191       value.clear();
192 
193     le->setText( value );
194   }
195 }
196 
QgsHanaSourceSelect(QWidget * parent,Qt::WindowFlags fl,QgsProviderRegistry::WidgetMode theWidgetMode)197 QgsHanaSourceSelect::QgsHanaSourceSelect(
198   QWidget *parent,
199   Qt::WindowFlags fl,
200   QgsProviderRegistry::WidgetMode theWidgetMode )
201   : QgsAbstractDataSourceWidget( parent, fl, theWidgetMode )
202 {
203   setupUi( this );
204   QgsGui::instance()->enableAutoGeometryRestore( this );
205 
206   connect( btnConnect, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnConnect_clicked );
207   connect( cbxAllowGeometrylessTables, &QCheckBox::stateChanged, this, &QgsHanaSourceSelect::cbxAllowGeometrylessTables_stateChanged );
208   connect( btnNew, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnNew_clicked );
209   connect( btnEdit, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnEdit_clicked );
210   connect( btnDelete, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnDelete_clicked );
211   connect( btnSave, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnSave_clicked );
212   connect( btnLoad, &QPushButton::clicked, this, &QgsHanaSourceSelect::btnLoad_clicked );
213   connect( mSearchGroupBox, &QGroupBox::toggled, this, &QgsHanaSourceSelect::mSearchGroupBox_toggled );
214   connect( mSearchTableEdit, &QLineEdit::textChanged, this, &QgsHanaSourceSelect::mSearchTableEdit_textChanged );
215   connect( mSearchColumnComboBox, &QComboBox::currentTextChanged, this, &QgsHanaSourceSelect::mSearchColumnComboBox_currentTextChanged );
216   connect( mSearchModeComboBox, &QComboBox::currentTextChanged, this, &QgsHanaSourceSelect::mSearchModeComboBox_currentTextChanged );
217   connect( cmbConnections, static_cast<void ( QComboBox::* )( int )>( &QComboBox::activated ),
218            this, &QgsHanaSourceSelect::cmbConnections_activated );
219   connect( mTablesTreeView, &QTreeView::clicked, this, &QgsHanaSourceSelect::mTablesTreeView_clicked );
220   connect( mTablesTreeView, &QTreeView::doubleClicked, this, &QgsHanaSourceSelect::mTablesTreeView_doubleClicked );
221   setupButtons( buttonBox );
222   connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsHanaSourceSelect::showHelp );
223 
224   if ( widgetMode() != QgsProviderRegistry::WidgetMode::None )
225     mHoldDialogOpen->hide();
226   else
227     setWindowTitle( tr( "Add SAP HANA Table(s)" ) );
228 
229   mBuildQueryButton = new QPushButton( tr( "&Set Filter" ) );
230   mBuildQueryButton->setToolTip( tr( "Set Filter" ) );
231   mBuildQueryButton->setDisabled( true );
232 
233   if ( widgetMode() != QgsProviderRegistry::WidgetMode::Manager )
234   {
235     buttonBox->addButton( mBuildQueryButton, QDialogButtonBox::ActionRole );
236     connect( mBuildQueryButton, &QAbstractButton::clicked, this, &QgsHanaSourceSelect::buildQuery );
237   }
238 
239   populateConnectionList();
240 
241   mSearchModeComboBox->addItem( tr( "Wildcard" ) );
242   mSearchModeComboBox->addItem( tr( "RegExp" ) );
243 
244   mSearchColumnComboBox->addItem( tr( "All" ) );
245   mSearchColumnComboBox->addItem( tr( "Schema" ) );
246   mSearchColumnComboBox->addItem( tr( "Table" ) );
247   mSearchColumnComboBox->addItem( tr( "Comment" ) );
248   mSearchColumnComboBox->addItem( tr( "Geometry column" ) );
249   mSearchColumnComboBox->addItem( tr( "Type" ) );
250   mSearchColumnComboBox->addItem( tr( "Feature id" ) );
251   mSearchColumnComboBox->addItem( tr( "SRID" ) );
252   mSearchColumnComboBox->addItem( tr( "Sql" ) );
253 
254   mProxyModel.setParent( this );
255   mProxyModel.setFilterKeyColumn( -1 );
256   mProxyModel.setFilterCaseSensitivity( Qt::CaseInsensitive );
257   mProxyModel.setSourceModel( &mTableModel );
258 
259   mTablesTreeView->setModel( &mProxyModel );
260   mTablesTreeView->setSortingEnabled( true );
261   mTablesTreeView->setEditTriggers( QAbstractItemView::CurrentChanged );
262   mTablesTreeView->setItemDelegate( new QgsHanaSourceSelectDelegate( this ) );
263 
264   connect( mTablesTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
265            this, &QgsHanaSourceSelect::treeWidgetSelectionChanged );
266 
267   const QgsSettings settings;
268   mTablesTreeView->setSelectionMode( settings.value( QStringLiteral( "qgis/addHanaDC" ), false ).toBool() ?
269                                      QAbstractItemView::ExtendedSelection : QAbstractItemView::MultiSelection );
270 
271   //for Qt < 4.3.2, passing -1 to include all model columns
272   //in search does not seem to work
273   mSearchColumnComboBox->setCurrentIndex( 2 );
274 
275   restoreGeometry( settings.value( QStringLiteral( "Windows/HanaSourceSelect/geometry" ) ).toByteArray() );
276   mHoldDialogOpen->setChecked( settings.value( QStringLiteral( "Windows/HanaSourceSelect/HoldDialogOpen" ), false ).toBool() );
277 
278   for ( int i = 0; i < mTableModel.columnCount(); i++ )
279   {
280     mTablesTreeView->setColumnWidth( i, settings.value( QStringLiteral( "Windows/HanaSourceSelect/columnWidths/%1" )
281                                      .arg( i ), mTablesTreeView->columnWidth( i ) ).toInt() );
282   }
283 
284   //hide the search options by default
285   //they will be shown when the user ticks
286   //the search options group box
287   mSearchLabel->setVisible( false );
288   mSearchColumnComboBox->setVisible( false );
289   mSearchColumnsLabel->setVisible( false );
290   mSearchModeComboBox->setVisible( false );
291   mSearchModeLabel->setVisible( false );
292   mSearchTableEdit->setVisible( false );
293 
294   cbxAllowGeometrylessTables->setDisabled( true );
295 }
296 
297 //! Autoconnected SLOTS *
298 // Slot for adding a new connection
btnNew_clicked()299 void QgsHanaSourceSelect::btnNew_clicked()
300 {
301   QgsHanaNewConnection nc( this );
302   if ( nc.exec() )
303   {
304     populateConnectionList();
305     emit connectionsChanged();
306   }
307 }
308 // Slot for deleting an existing connection
btnDelete_clicked()309 void QgsHanaSourceSelect::btnDelete_clicked()
310 {
311   const QString msg = tr( "Are you sure you want to remove the %1 connection and all associated settings?" )
312                       .arg( cmbConnections->currentText() );
313   if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Confirm Delete" ), msg, QMessageBox::Yes | QMessageBox::No ) )
314     return;
315 
316   QgsHanaSourceSelect::deleteConnection( cmbConnections->currentText() );
317 
318   populateConnectionList();
319   emit connectionsChanged();
320 }
321 
deleteConnection(const QString & name)322 void QgsHanaSourceSelect::deleteConnection( const QString &name )
323 {
324   QgsHanaSettings::removeConnection( name );
325 }
326 
btnSave_clicked()327 void QgsHanaSourceSelect::btnSave_clicked()
328 {
329   QgsManageConnectionsDialog dlg( this, QgsManageConnectionsDialog::Export, QgsManageConnectionsDialog::HANA );
330   dlg.exec();
331 }
332 
btnLoad_clicked()333 void QgsHanaSourceSelect::btnLoad_clicked()
334 {
335   const QString fileName = QFileDialog::getOpenFileName( this, tr( "Load Connections" ),
336                            QDir::homePath(), tr( "XML files (*.xml *XML)" ) );
337   if ( fileName.isEmpty() )
338   {
339     return;
340   }
341 
342   QgsManageConnectionsDialog dlg( this, QgsManageConnectionsDialog::Import,
343                                   QgsManageConnectionsDialog::HANA, fileName );
344   dlg.exec();
345   populateConnectionList();
346 }
347 
348 // Slot for editing a connection
btnEdit_clicked()349 void QgsHanaSourceSelect::btnEdit_clicked()
350 {
351   QgsHanaNewConnection nc( this, cmbConnections->currentText() );
352   if ( nc.exec() )
353   {
354     populateConnectionList();
355     emit connectionsChanged();
356   }
357 }
358 
359 //! End Autoconnected SLOTS *
360 
361 // Remember which database is selected
cmbConnections_activated(int)362 void QgsHanaSourceSelect::cmbConnections_activated( int )
363 {
364   // Remember which database was selected.
365   QgsHanaSettings::setSelectedConnection( cmbConnections->currentText() );
366 
367   cbxAllowGeometrylessTables->blockSignals( true );
368   QgsHanaSettings settings( cmbConnections->currentText() );
369   settings.load();
370   cbxAllowGeometrylessTables->setChecked( settings.allowGeometrylessTables() );
371   cbxAllowGeometrylessTables->blockSignals( false );
372 }
373 
cbxAllowGeometrylessTables_stateChanged(int)374 void QgsHanaSourceSelect::cbxAllowGeometrylessTables_stateChanged( int )
375 {
376   btnConnect_clicked();
377 }
378 
buildQuery()379 void QgsHanaSourceSelect::buildQuery()
380 {
381   setSql( mTablesTreeView->currentIndex() );
382 }
383 
mTablesTreeView_clicked(const QModelIndex & index)384 void QgsHanaSourceSelect::mTablesTreeView_clicked( const QModelIndex &index )
385 {
386   mBuildQueryButton->setEnabled( index.parent().isValid() );
387 }
388 
mTablesTreeView_doubleClicked(const QModelIndex & index)389 void QgsHanaSourceSelect::mTablesTreeView_doubleClicked( const QModelIndex &index )
390 {
391   const QgsSettings settings;
392   if ( settings.value( QStringLiteral( "qgis/addHANADC" ), false ).toBool() )
393     addButtonClicked();
394   else
395     setSql( index );
396 }
397 
mSearchGroupBox_toggled(bool checked)398 void QgsHanaSourceSelect::mSearchGroupBox_toggled( bool checked )
399 {
400   if ( mSearchTableEdit->text().isEmpty() )
401     return;
402 
403   mSearchTableEdit_textChanged( checked ? mSearchTableEdit->text() : QString( ) );
404 }
405 
mSearchTableEdit_textChanged(const QString & text)406 void QgsHanaSourceSelect::mSearchTableEdit_textChanged( const QString &text )
407 {
408   if ( mSearchModeComboBox->currentText() == tr( "Wildcard" ) )
409     mProxyModel._setFilterWildcard( text );
410   else if ( mSearchModeComboBox->currentText() == tr( "RegExp" ) )
411     mProxyModel._setFilterRegExp( text );
412 }
413 
mSearchColumnComboBox_currentTextChanged(const QString & text)414 void QgsHanaSourceSelect::mSearchColumnComboBox_currentTextChanged( const QString &text )
415 {
416   if ( text == tr( "All" ) )
417   {
418     mProxyModel.setFilterKeyColumn( -1 );
419   }
420   else if ( text == tr( "Schema" ) )
421   {
422     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmSchema );
423   }
424   else if ( text == tr( "Table" ) )
425   {
426     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmTable );
427   }
428   else if ( text == tr( "Comment" ) )
429   {
430     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmComment );
431   }
432   else if ( text == tr( "Geometry column" ) )
433   {
434     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmGeomCol );
435   }
436   else if ( text == tr( "Type" ) )
437   {
438     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmGeomType );
439   }
440   else if ( text == tr( "Feature id" ) )
441   {
442     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmPkCol );
443   }
444   else if ( text == tr( "SRID" ) )
445   {
446     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmSrid );
447   }
448   else if ( text == tr( "Sql" ) )
449   {
450     mProxyModel.setFilterKeyColumn( QgsHanaTableModel::DbtmSql );
451   }
452 }
453 
mSearchModeComboBox_currentTextChanged(const QString & text)454 void QgsHanaSourceSelect::mSearchModeComboBox_currentTextChanged( const QString &text )
455 {
456   Q_UNUSED( text );
457   mSearchTableEdit_textChanged( mSearchTableEdit->text() );
458 }
459 
setLayerType(const QgsHanaLayerProperty & layerProperty)460 void QgsHanaSourceSelect::setLayerType( const QgsHanaLayerProperty &layerProperty )
461 {
462   mTableModel.addTableEntry( mConnectionName, layerProperty );
463 }
464 
~QgsHanaSourceSelect()465 QgsHanaSourceSelect::~QgsHanaSourceSelect()
466 {
467   if ( mColumnTypeThread )
468   {
469     mColumnTypeThread->requestInterruption();
470     mColumnTypeThread->wait();
471     finishList();
472   }
473 
474   QgsSettings settings;
475   settings.setValue( QStringLiteral( "Windows/HanaSourceSelect/geometry" ), saveGeometry() );
476   settings.setValue( QStringLiteral( "Windows/HanaSourceSelect/HoldDialogOpen" ), mHoldDialogOpen->isChecked() );
477 
478   for ( int i = 0; i < mTableModel.columnCount(); i++ )
479   {
480     settings.setValue( QStringLiteral( "Windows/HanaSourceSelect/columnWidths/%1" )
481                        .arg( i ), mTablesTreeView->columnWidth( i ) );
482   }
483 }
484 
populateConnectionList()485 void QgsHanaSourceSelect::populateConnectionList()
486 {
487   cmbConnections->blockSignals( true );
488   cmbConnections->clear();
489   cmbConnections->addItems( QgsHanaSettings::getConnectionNames() );
490   cmbConnections->blockSignals( false );
491 
492   setConnectionListPosition();
493 
494   btnEdit->setDisabled( cmbConnections->count() == 0 );
495   btnDelete->setDisabled( cmbConnections->count() == 0 );
496   btnConnect->setDisabled( cmbConnections->count() == 0 );
497   cmbConnections->setDisabled( cmbConnections->count() == 0 );
498 }
499 
selectedTables()500 QStringList QgsHanaSourceSelect::selectedTables()
501 {
502   return mSelectedTables;
503 }
504 
connectionInfo()505 QString QgsHanaSourceSelect::connectionInfo()
506 {
507   return mConnectionInfo;
508 }
509 
510 // Slot for performing action when the Add button is clicked
addButtonClicked()511 void QgsHanaSourceSelect::addButtonClicked()
512 {
513   mSelectedTables.clear();
514 
515   const QModelIndexList indexes = mTablesTreeView->selectionModel()->selection().indexes();
516   for ( const QModelIndex &idx : indexes )
517   {
518     if ( idx.column() != QgsHanaTableModel::DbtmTable )
519       continue;
520 
521     const QString uri = mTableModel.layerURI( mProxyModel.mapToSource( idx ), mConnectionName, mConnectionInfo );
522     if ( uri.isNull() )
523       continue;
524 
525     mSelectedTables << uri;
526   }
527 
528   if ( mSelectedTables.empty() )
529   {
530     QMessageBox::information( this, tr( "Select Table" ), tr( "You must select a table in order to add a layer." ) );
531   }
532   else
533   {
534     emit addDatabaseLayers( mSelectedTables, QStringLiteral( "hana" ) );
535     if ( !mHoldDialogOpen->isChecked() && widgetMode() == QgsProviderRegistry::WidgetMode::None )
536       accept();
537   }
538 }
539 
btnConnect_clicked()540 void QgsHanaSourceSelect::btnConnect_clicked()
541 {
542   cbxAllowGeometrylessTables->setEnabled( true );
543 
544   if ( mColumnTypeThread )
545   {
546     mColumnTypeThread->requestInterruption();
547     mColumnTypeThread->wait();
548     return;
549   }
550 
551   const QString connName = cmbConnections->currentText();
552 
553   const QModelIndex rootItemIndex = mTableModel.indexFromItem( mTableModel.invisibleRootItem() );
554   mTableModel.removeRows( 0, mTableModel.rowCount( rootItemIndex ), rootItemIndex );
555 
556   QgsHanaSettings settings( connName, true );
557   settings.setAllowGeometrylessTables( cbxAllowGeometrylessTables->isChecked() );
558 
559   const QgsDataSourceUri uri = settings.toDataSourceUri();
560   bool canceled = false;
561 
562   const std::unique_ptr<QgsHanaConnection> conn( QgsHanaConnection::createConnection( uri, &canceled, nullptr ) );
563   if ( !conn )
564   {
565     if ( !canceled )
566       QMessageBox::warning( this, tr( "SAP HANA" ), tr( "Unable to connect to a database" ) );
567     return;
568   }
569 
570   mConnectionName = connName;
571   mConnectionInfo = QgsHanaUtils::connectionInfo( uri );
572 
573   QApplication::setOverrideCursor( Qt::BusyCursor );
574 
575   mColumnTypeThread = std::make_unique<QgsHanaColumnTypeThread>( mConnectionName, uri, settings.allowGeometrylessTables(), settings.userTablesOnly() );
576   mColumnTypeTask = std::make_unique<QgsProxyProgressTask>( tr( "Scanning tables for %1" ).arg( mConnectionName ) );
577   QgsApplication::taskManager()->addTask( mColumnTypeTask.get() );
578 
579   connect( mColumnTypeThread.get(), &QgsHanaColumnTypeThread::setLayerType,
580            this, &QgsHanaSourceSelect::setLayerType );
581   connect( mColumnTypeThread.get(), &QThread::finished,
582            this, &QgsHanaSourceSelect::columnThreadFinished );
583   connect( mColumnTypeThread.get(), &QgsHanaColumnTypeThread::progress,
584            mColumnTypeTask.get(), [&]( int i, int n )
585   {
586     mColumnTypeTask->setProxyProgress( 100.0 * static_cast<double>( i ) / n );
587   } );
588   connect( mColumnTypeThread.get(), &QgsHanaColumnTypeThread::progressMessage,
589            this, &QgsHanaSourceSelect::progressMessage );
590 
591   btnConnect->setText( tr( "Stop" ) );
592   mColumnTypeThread->start();
593 }
594 
finishList()595 void QgsHanaSourceSelect::finishList()
596 {
597   QApplication::restoreOverrideCursor();
598 
599   mTablesTreeView->sortByColumn( QgsHanaTableModel::DbtmTable, Qt::AscendingOrder );
600   mTablesTreeView->sortByColumn( QgsHanaTableModel::DbtmSchema, Qt::AscendingOrder );
601 }
602 
columnThreadFinished()603 void QgsHanaSourceSelect::columnThreadFinished()
604 {
605   const QString errorMsg = mColumnTypeThread->errorMessage();
606   mColumnTypeThread.reset( nullptr );
607   QgsProxyProgressTask *task = mColumnTypeTask.release();
608   task->finalize( errorMsg.isEmpty() );
609   if ( !errorMsg.isEmpty() )
610     pushMessage( tr( "Failed to retrieve tables for %1" ).arg( mConnectionName ), errorMsg, Qgis::MessageLevel::Warning );
611 
612   btnConnect->setText( tr( "Connect" ) );
613 
614   finishList();
615 }
616 
refresh()617 void QgsHanaSourceSelect::refresh()
618 {
619   populateConnectionList();
620 }
621 
setSql(const QModelIndex & index)622 void QgsHanaSourceSelect::setSql( const QModelIndex &index )
623 {
624   if ( !index.parent().isValid() )
625   {
626     QgsDebugMsg( "schema item found" );
627     return;
628   }
629 
630   const QModelIndex idx = mProxyModel.mapToSource( index );
631   const QString uri = mTableModel.layerURI( idx, mConnectionName, mConnectionInfo );
632   if ( uri.isNull() )
633   {
634     QgsDebugMsg( "no uri" );
635     return;
636   }
637 
638   const QString tableName = mTableModel.itemFromIndex( idx.sibling( idx.row(), QgsHanaTableModel::DbtmTable ) )->text();
639 
640   QgsVectorLayer vlayer( uri, tableName, QStringLiteral( "hana" ) );
641   if ( !vlayer.isValid() )
642     return;
643 
644   QgsQueryBuilder gb( &vlayer, this );
645   if ( gb.exec() )
646     mTableModel.setSql( mProxyModel.mapToSource( index ), gb.sql() );
647 }
648 
fullDescription(const QString & schema,const QString & table,const QString & column,const QString & type)649 QString QgsHanaSourceSelect::fullDescription(
650   const QString &schema, const QString &table, const QString &column, const QString &type )
651 {
652   QString desc;
653   if ( !schema.isEmpty() )
654     desc = schema + '.';
655   desc += table + " (" + column + ") " + type;
656   return desc;
657 }
658 
setConnectionListPosition()659 void QgsHanaSourceSelect::setConnectionListPosition()
660 {
661   const QString selectedConnName = QgsHanaSettings::getSelectedConnection();
662   cmbConnections->setCurrentIndex( cmbConnections->findText( selectedConnName ) );
663   if ( cmbConnections->currentIndex() < 0 )
664     cmbConnections->setCurrentIndex( selectedConnName.isNull() ? 0 : cmbConnections->count() - 1 );
665 }
666 
setSearchExpression(const QString & regexp)667 void QgsHanaSourceSelect::setSearchExpression( const QString &regexp )
668 {
669   Q_UNUSED( regexp )
670 }
671 
treeWidgetSelectionChanged(const QItemSelection & selected,const QItemSelection & deselected)672 void QgsHanaSourceSelect::treeWidgetSelectionChanged(
673   const QItemSelection &selected, const QItemSelection &deselected )
674 {
675   Q_UNUSED( deselected )
676   emit enableButtons( !selected.isEmpty() );
677 }
678 
showHelp()679 void QgsHanaSourceSelect::showHelp()
680 {
681   QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#loading-a-database-layer" ) );
682 }
683