1 /***************************************************************************
2 qgsdatabasetablecombobox.cpp
3 --------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsdatabasetablecombobox.h"
17 #include "qgsdatabasetablemodel.h"
18 #include "qgsapplication.h"
19 #include "qgsabstractdatabaseproviderconnection.h"
20 #include <QHBoxLayout>
21 #include <QToolButton>
22
QgsDatabaseTableComboBox(const QString & provider,const QString & connection,const QString & schema,QWidget * parent)23 QgsDatabaseTableComboBox::QgsDatabaseTableComboBox( const QString &provider, const QString &connection, const QString &schema, QWidget *parent )
24 : QWidget( parent )
25 , mProvider( provider )
26 , mConnection( connection )
27 , mSchema( schema )
28 {
29 if ( !provider.isEmpty() && !connection.isEmpty() )
30 mModel = new QgsDatabaseTableModel( provider, connection, schema, this );
31 init();
32 }
33
QgsDatabaseTableComboBox(QgsAbstractDatabaseProviderConnection * connection,const QString & schema,QWidget * parent)34 QgsDatabaseTableComboBox::QgsDatabaseTableComboBox( QgsAbstractDatabaseProviderConnection *connection, const QString &schema, QWidget *parent )
35 : QWidget( parent )
36 , mSchema( schema )
37 {
38 mModel = new QgsDatabaseTableModel( connection, schema, this );
39 init();
40 }
41
setAllowEmptyTable(bool allowEmpty)42 void QgsDatabaseTableComboBox::setAllowEmptyTable( bool allowEmpty )
43 {
44 mAllowEmpty = allowEmpty;
45 if ( mModel )
46 mModel->setAllowEmptyTable( allowEmpty );
47 }
48
allowEmptyTable() const49 bool QgsDatabaseTableComboBox::allowEmptyTable() const
50 {
51 return mAllowEmpty;
52 }
53
init()54 void QgsDatabaseTableComboBox::init()
55 {
56 mComboBox = new QComboBox();
57
58 mSortModel = new QgsDatabaseTableComboBoxSortModel( this );
59 if ( mModel )
60 mSortModel->setSourceModel( mModel );
61 mSortModel->setSortRole( Qt::DisplayRole );
62 mSortModel->setSortLocaleAware( true );
63 mSortModel->setSortCaseSensitivity( Qt::CaseInsensitive );
64 mSortModel->setDynamicSortFilter( true );
65 mSortModel->sort( 0 );
66
67 mComboBox->setModel( mSortModel );
68
69 QHBoxLayout *l = new QHBoxLayout();
70 l->setContentsMargins( 0, 0, 0, 0 );
71 l->addWidget( mComboBox );
72
73 QToolButton *refreshButton = new QToolButton();
74 refreshButton->setAutoRaise( true );
75 refreshButton->setToolTip( tr( "Refresh tables" ) );
76 refreshButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionRefresh.svg" ) ) );
77 l->addWidget( refreshButton );
78 setLayout( l );
79
80 connect( refreshButton, &QToolButton::clicked, this, &QgsDatabaseTableComboBox::refreshTables );
81
82 connect( mComboBox, static_cast < void ( QComboBox::* )( int ) > ( &QComboBox::activated ), this, &QgsDatabaseTableComboBox::indexChanged );
83 connect( mSortModel, &QAbstractItemModel::rowsInserted, this, &QgsDatabaseTableComboBox::rowsChanged );
84 connect( mSortModel, &QAbstractItemModel::rowsRemoved, this, &QgsDatabaseTableComboBox::rowsChanged );
85 }
86
setTable(const QString & table,const QString & schema)87 void QgsDatabaseTableComboBox::setTable( const QString &table, const QString &schema )
88 {
89 if ( schema == currentSchema() && table == currentTable() )
90 return;
91
92 if ( table.isEmpty() )
93 {
94 if ( mAllowEmpty )
95 mComboBox->setCurrentIndex( 0 );
96 else
97 mComboBox->setCurrentIndex( -1 );
98
99 emit tableChanged( QString() );
100 return;
101 }
102
103 const QModelIndexList idxs = mSortModel->match( mSortModel->index( 0, 0 ), QgsDatabaseTableModel::RoleTableName, table, -1, Qt::MatchFixedString | Qt::MatchCaseSensitive );
104 for ( const QModelIndex &proxyIdx : idxs )
105 {
106 if ( proxyIdx.isValid() && proxyIdx.data( QgsDatabaseTableModel::RoleTableName ).toString() == table
107 && ( schema.isEmpty() || proxyIdx.data( QgsDatabaseTableModel::RoleSchema ).toString() == schema ) )
108 {
109 mComboBox->setCurrentIndex( proxyIdx.row() );
110 emit tableChanged( currentTable(), currentSchema() );
111 return;
112 }
113 }
114 mComboBox->setCurrentIndex( -1 );
115 emit tableChanged( QString() );
116 }
117
setConnectionName(const QString & connection,const QString & provider)118 void QgsDatabaseTableComboBox::setConnectionName( const QString &connection, const QString &provider )
119 {
120 if ( provider.isEmpty() && mConnection == connection )
121 return;
122
123 if ( !provider.isEmpty() )
124 mProvider = provider;
125
126 mConnection = connection;
127
128 const QString oldTable = currentTable();
129 const QString oldSchema = currentSchema();
130 QgsDatabaseTableModel *oldModel = mModel;
131 if ( !mConnection.isEmpty() )
132 {
133 mModel = new QgsDatabaseTableModel( mProvider, mConnection, mSchema, this );
134 mModel->setAllowEmptyTable( mAllowEmpty );
135 }
136 else
137 mModel = nullptr;
138
139 mSortModel->setSourceModel( mModel );
140 if ( oldModel )
141 oldModel->deleteLater();
142 if ( currentTable() != oldTable || currentSchema() != oldSchema )
143 setTable( oldTable, oldSchema );
144 }
145
setSchema(const QString & schema)146 void QgsDatabaseTableComboBox::setSchema( const QString &schema )
147 {
148 if ( schema == mSchema )
149 return;
150 mSchema = schema;
151
152 if ( !mProvider.isEmpty() && !mConnection.isEmpty() )
153 {
154 const QString oldTable = currentTable();
155 QgsDatabaseTableModel *oldModel = mModel;
156 mModel = new QgsDatabaseTableModel( mProvider, mConnection, mSchema, this );
157 mSortModel->setSourceModel( mModel );
158 oldModel->deleteLater();
159 setTable( oldTable );
160 }
161 }
162
refreshTables()163 void QgsDatabaseTableComboBox::refreshTables()
164 {
165 const QString oldSchema = currentSchema();
166 const QString oldTable = currentTable();
167 if ( mModel )
168 mModel->refresh();
169 setTable( oldTable, oldSchema );
170 }
171
currentSchema() const172 QString QgsDatabaseTableComboBox::currentSchema() const
173 {
174 const QModelIndex proxyIndex = mSortModel->index( mComboBox->currentIndex(), 0 );
175 if ( !proxyIndex.isValid() )
176 {
177 return QString();
178 }
179
180 return mSortModel->data( proxyIndex, QgsDatabaseTableModel::RoleSchema ).toString();
181 }
182
currentTable() const183 QString QgsDatabaseTableComboBox::currentTable() const
184 {
185 const QModelIndex proxyIndex = mSortModel->index( mComboBox->currentIndex(), 0 );
186 if ( !proxyIndex.isValid() )
187 {
188 return QString();
189 }
190
191 return mSortModel->data( proxyIndex, QgsDatabaseTableModel::RoleTableName ).toString();
192 }
193
indexChanged(int i)194 void QgsDatabaseTableComboBox::indexChanged( int i )
195 {
196 Q_UNUSED( i )
197 emit tableChanged( currentTable() );
198 }
199
rowsChanged()200 void QgsDatabaseTableComboBox::rowsChanged()
201 {
202 if ( mComboBox->count() == 1 || ( mAllowEmpty && mComboBox->count() == 2 && mComboBox->currentIndex() == 1 ) )
203 {
204 //currently selected connection item has changed
205 emit tableChanged( currentTable(), currentSchema() );
206 }
207 else if ( mComboBox->count() == 0 )
208 {
209 emit tableChanged( QString() );
210 }
211 }
212
213 ///@cond PRIVATE
QgsDatabaseTableComboBoxSortModel(QObject * parent)214 QgsDatabaseTableComboBoxSortModel::QgsDatabaseTableComboBoxSortModel( QObject *parent )
215 : QSortFilterProxyModel( parent )
216 {
217
218 }
219
lessThan(const QModelIndex & left,const QModelIndex & right) const220 bool QgsDatabaseTableComboBoxSortModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
221 {
222 // empty row is always first
223 if ( sourceModel()->data( left, QgsDatabaseTableModel::RoleEmpty ).toBool() )
224 return true;
225 else if ( sourceModel()->data( right, QgsDatabaseTableModel::RoleEmpty ).toBool() )
226 return false;
227
228 // default mode is alphabetical order
229 const QString leftStr = sourceModel()->data( left ).toString();
230 const QString rightStr = sourceModel()->data( right ).toString();
231 return QString::localeAwareCompare( leftStr, rightStr ) < 0;
232 }
233
234 ///@endcond
235