QGIS API Documentation  3.13.0-Master (740be229cb)
qgsdatasourceselectdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdatasourceselectdialog.cpp - QgsDataSourceSelectDialog
3 
4  ---------------------
5  begin : 1.11.2018
6  copyright : (C) 2018 by Alessandro Pasotti
7  email : [email protected]
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
18 
19 #include "qgis.h"
20 #include "qgsbrowsermodel.h"
21 #include "qgsgui.h"
22 #include "qgsguiutils.h"
23 #include "qgssettings.h"
24 #include "qgsnative.h"
25 
26 #include <QPushButton>
27 #include <QMenu>
28 #include <QDesktopServices>
29 #include <QDialogButtonBox>
30 
32  QgsBrowserGuiModel *browserModel,
33  bool setFilterByLayerType,
34  QgsMapLayerType layerType,
35  QWidget *parent )
36  : QgsPanelWidget( parent )
37 {
38  if ( ! browserModel )
39  {
40  mBrowserModel = new QgsBrowserGuiModel( this );
41  mBrowserModel->initialize();
42  }
43  else
44  {
45  mBrowserModel = browserModel;
46  mBrowserModel->initialize();
47  }
48 
49  setupUi( this );
50 
51  mBrowserProxyModel.setBrowserModel( mBrowserModel );
52  mBrowserTreeView->setHeaderHidden( true );
53 
54  if ( setFilterByLayerType )
55  {
56  // This will also set the (proxy) model
57  setLayerTypeFilter( layerType );
58  }
59  else
60  {
61  mBrowserTreeView->setModel( &mBrowserProxyModel );
62  setValid( false );
63  }
64 
65  mBrowserTreeView->setBrowserModel( mBrowserModel );
66 
67  mWidgetFilter->hide();
68  mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
69  // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
70 
71  QMenu *menu = new QMenu( this );
72  menu->setSeparatorsCollapsible( false );
73  mBtnFilterOptions->setMenu( menu );
74  QAction *action = new QAction( tr( "Case Sensitive" ), menu );
75  action->setData( "case" );
76  action->setCheckable( true );
77  action->setChecked( false );
78  connect( action, &QAction::toggled, this, &QgsDataSourceSelectWidget::setCaseSensitive );
79  menu->addAction( action );
80  QActionGroup *group = new QActionGroup( menu );
81  action = new QAction( tr( "Filter Pattern Syntax" ), group );
82  action->setSeparator( true );
83  menu->addAction( action );
84  action = new QAction( tr( "Normal" ), group );
85  action->setData( QgsBrowserProxyModel::Normal );
86  action->setCheckable( true );
87  action->setChecked( true );
88  menu->addAction( action );
89  action = new QAction( tr( "Wildcard(s)" ), group );
90  action->setData( QgsBrowserProxyModel::Wildcards );
91  action->setCheckable( true );
92  menu->addAction( action );
93  action = new QAction( tr( "Regular Expression" ), group );
94  action->setData( QgsBrowserProxyModel::RegularExpression );
95  action->setCheckable( true );
96  menu->addAction( action );
97 
98  connect( mActionRefresh, &QAction::triggered, this, [ = ] { refreshModel( QModelIndex() ); } );
99  connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectWidget::onLayerSelected );
100  connect( mBrowserTreeView, &QgsBrowserTreeView::doubleClicked, this, &QgsDataSourceSelectWidget::itemDoubleClicked );
101  connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
102  connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectWidget::showFilterWidget );
103  connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectWidget::setFilter );
105  connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectWidget::setFilter );
106  connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectWidget::setFilterSyntax );
107 
108  mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
109 
110  if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
111  {
112  mActionShowFilter->trigger();
113  }
114 }
115 
117 
119 {
120  QgsPanelWidget::showEvent( e );
121  QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
122  QString(), QgsSettings::Section::Gui ).toString() );
123  if ( ! lastSelectedPath.isEmpty() )
124  {
125  QModelIndexList items = mBrowserProxyModel.match(
126  mBrowserProxyModel.index( 0, 0 ),
128  QVariant::fromValue( lastSelectedPath ),
129  1,
130  Qt::MatchRecursive );
131  if ( items.count( ) > 0 )
132  {
133  QModelIndex expandIndex = items.at( 0 );
134  if ( expandIndex.isValid() )
135  {
136  mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
137  mBrowserTreeView->expand( expandIndex );
138  }
139  }
140  }
141 }
142 
144 {
145  QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
146  mWidgetFilter->setVisible( visible );
147  if ( ! visible )
148  {
149  mLeFilter->setText( QString() );
150  setFilter();
151  }
152  else
153  {
154  mLeFilter->setFocus();
155  }
156 }
157 
158 void QgsDataSourceSelectWidget::setDescription( const QString &description )
159 {
160  if ( !description.isEmpty() )
161  {
162  if ( !mDescriptionLabel )
163  {
164  mDescriptionLabel = new QLabel();
165  mDescriptionLabel->setWordWrap( true );
166  mDescriptionLabel->setMargin( 4 );
167  mDescriptionLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
168  connect( mDescriptionLabel, &QLabel::linkActivated, this, [ = ]( const QString & link )
169  {
170  QUrl url( link );
171  QFileInfo file( url.toLocalFile() );
172  if ( file.exists() && !file.isDir() )
173  QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
174  else
175  QDesktopServices::openUrl( url );
176  } );
177  verticalLayout->insertWidget( 1, mDescriptionLabel );
178  }
179  mDescriptionLabel->setText( description );
180  }
181  else
182  {
183  if ( mDescriptionLabel )
184  {
185  verticalLayout->removeWidget( mDescriptionLabel );
186  delete mDescriptionLabel;
187  mDescriptionLabel = nullptr;
188  }
189  }
190 }
191 
193 {
194  QString filter = mLeFilter->text();
195  mBrowserProxyModel.setFilterString( filter );
196 }
197 
198 
199 void QgsDataSourceSelectWidget::refreshModel( const QModelIndex &index )
200 {
201 
202  QgsDataItem *item = mBrowserModel->dataItem( index );
203  if ( item )
204  {
205  QgsDebugMsgLevel( "path = " + item->path(), 2 );
206  }
207  else
208  {
209  QgsDebugMsg( QStringLiteral( "invalid item" ) );
210  }
211 
212  if ( item && ( item->capabilities2() & QgsDataItem::Fertile ) )
213  {
214  mBrowserModel->refresh( index );
215  }
216 
217  for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
218  {
219  QModelIndex idx = mBrowserModel->index( i, 0, index );
220  QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
221  QgsDataItem *child = mBrowserModel->dataItem( idx );
222 
223  // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
224  // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
225  if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & QgsDataItem::Fast ) )
226  {
227  refreshModel( idx );
228  }
229  else
230  {
231  if ( child && ( child->capabilities2() & QgsDataItem::Fertile ) )
232  {
233  child->depopulate();
234  }
235  }
236  }
237 }
238 
239 void QgsDataSourceSelectWidget::setValid( bool valid )
240 {
241  const bool prev = mIsValid;
242  mIsValid = valid;
243  if ( prev != mIsValid )
244  emit validationChanged( mIsValid );
245 
246 }
247 
248 
250 {
251  if ( !action )
252  return;
253  mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
254 }
255 
257 {
258  mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
259 }
260 
262 {
263  mBrowserProxyModel.setFilterByLayerType( true );
264  mBrowserProxyModel.setLayerType( layerType );
265  // reset model and button
266  mBrowserTreeView->setModel( &mBrowserProxyModel );
267  setValid( false );
268 }
269 
271 {
272  return mUri;
273 }
274 
275 void QgsDataSourceSelectWidget::onLayerSelected( const QModelIndex &index )
276 {
277  bool isLayerCompatible = false;
278  mUri = QgsMimeDataUtils::Uri();
279  if ( index.isValid() )
280  {
281  const QgsDataItem *dataItem( mBrowserProxyModel.dataItem( index ) );
282  if ( dataItem )
283  {
284  const QgsLayerItem *layerItem = qobject_cast<const QgsLayerItem *>( dataItem );
285  if ( layerItem && ( ! mBrowserProxyModel.filterByLayerType() ||
286  ( layerItem->mapLayerType() == mBrowserProxyModel.layerType() ) ) )
287  {
288  isLayerCompatible = true;
289  mUri = layerItem->mimeUri();
290  // Store last viewed item
291  QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, QgsBrowserGuiModel::PathRole ).toString(), QgsSettings::Section::Gui );
292  }
293  }
294  }
295  setValid( isLayerCompatible );
296  emit selectionChanged();
297 }
298 
299 void QgsDataSourceSelectWidget::itemDoubleClicked( const QModelIndex &index )
300 {
301  onLayerSelected( index );
302  if ( mIsValid )
303  emit itemTriggered( uri() );
304 }
305 
306 //
307 // QgsDataSourceSelectDialog
308 //
309 
310 QgsDataSourceSelectDialog::QgsDataSourceSelectDialog( QgsBrowserGuiModel *browserModel, bool setFilterByLayerType, QgsMapLayerType layerType, QWidget *parent )
311  : QDialog( parent )
312 {
313  setWindowTitle( tr( "Select a Data Source" ) );
314  setObjectName( QStringLiteral( "QgsDataSourceSelectDialog" ) );
316 
317  mWidget = new QgsDataSourceSelectWidget( browserModel, setFilterByLayerType, layerType );
318 
319  QVBoxLayout *vl = new QVBoxLayout();
320  vl->addWidget( mWidget, 1 );
321  vl->setContentsMargins( 4, 4, 4, 4 );
322  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
323  connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
324  connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
325  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
326  connect( mWidget, &QgsDataSourceSelectWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
327  connect( mWidget, &QgsDataSourceSelectWidget::itemTriggered, this, &QDialog::accept );
328  vl->addWidget( buttonBox );
329  setLayout( vl );
330 }
331 
333 {
334  mWidget->setLayerTypeFilter( layerType );
335 }
336 
337 void QgsDataSourceSelectDialog::setDescription( const QString &description )
338 {
339  mWidget->setDescription( description );
340 }
341 
343 {
344  return mWidget->uri();
345 }
346 
348 {
349  mWidget->showFilterWidget( visible );
350 }
351 
353 {
354  mWidget->setFilterSyntax( syntax );
355 }
356 
358 {
359  mWidget->setCaseSensitive( caseSensitive );
360 }
361 
363 {
364  mWidget->setFilter();
365 
366 }
void showFilterWidget(bool visible)
Show/hide filter widget.
void setDescription(const QString &description)
Sets a description label.
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
QString path() const
Definition: qgsdataitem.h:327
void setLayerTypeFilter(QgsMapLayerType layerType)
Sets layer type filter to layerType and activates the filtering.
void setFilterSyntax(QAction *)
Sets filter syntax.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS&#39;s underlying public inte...
Definition: qgsgui.cpp:68
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setLayerType(QgsMapLayerType type)
Sets the layer type to filter the model by.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void setFilter()
Apply filter to the model.
QgsMapLayerType mapLayerType() const
Returns QgsMapLayerType.
void setDescription(const QString &description)
Sets a description label.
void setBrowserModel(QgsBrowserModel *model)
Sets the underlying browser model.
QgsMapLayerType layerType() const
Returns the layer type to filter the model by.
QgsDataSourceSelectDialog(QgsBrowserGuiModel *browserModel=nullptr, bool setFilterByLayerType=false, QgsMapLayerType layerType=QgsMapLayerType::VectorLayer, QWidget *parent=nullptr)
Constructs a QgsDataSourceSelectDialog, optionally filtering by layer type.
Base class for any widget that can be shown as a inline panel.
virtual void depopulate()
Remove children recursively and set as not populated. This is used when refreshing collapsed items...
A model for showing available data sources and other items in a structured tree.
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:62
void setFilterSyntax(FilterSyntax syntax)
Sets the filter syntax.
void setCaseSensitive(bool caseSensitive)
Sets filter case sensitivity.
Item path used to access path in the tree, see QgsDataItem::mPath.
void setCaseSensitive(bool caseSensitive)
Sets filter case sensitivity.
The QgsDataSourceSelectWidget class embeds the browser view to select an existing data source...
void validationChanged(bool isValid)
This signal is emitted whenever the validation status of the widget changes.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void showEvent(QShowEvent *e) override
Scroll to last selected index and expand it&#39;s children.
void itemTriggered(const QgsMimeDataUtils::Uri &uri)
Emitted when an item is triggered, e.g.
void setFilterString(const QString &filter)
Sets the filter string to use when filtering items in the model.
void initialize()
Delayed initialization, needed because the provider registry must be already populated.
QgsDataItem * dataItem(const QModelIndex &idx) const
Returns the data item at the specified index, or nullptr if no item exists at the index...
~QgsDataSourceSelectWidget() override
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
void setFilterCaseSensitivity(Qt::CaseSensitivity sensitivity)
Sets whether item filtering should be case sensitive.
bool filterByLayerType() const
Returns true if the model is filtered by map layer type.
Base class for all items in the model.
Definition: qgsdataitem.h:49
QgsDataSourceSelectWidget(QgsBrowserGuiModel *browserModel=nullptr, bool setFilterByLayerType=false, QgsMapLayerType layerType=QgsMapLayerType::VectorLayer, QWidget *parent=nullptr)
Constructs a QgsDataSourceSelectWidget, optionally filtering by layer type.
void setFilter()
Apply filter to the model.
Can create children. Even items without this capability may have children, but cannot create them...
Definition: qgsdataitem.h:242
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QgsMimeDataUtils::Uri mimeUri() const override
Returns mime URI for the data item.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void setFilterSyntax(QAction *)
Sets filter syntax.
void cleared()
Emitted when the widget is cleared.
void refresh(const QString &path)
Refresh item specified by path.
void selectionChanged()
Emitted when the current selection changes in the widget.
Standard string filtering.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:133
void showFilterWidget(bool visible)
Show/hide filter widget.
Item that represents a layer that can be opened with one of the providers.
Definition: qgsdataitem.h:492
void setFilterByLayerType(bool enabled)
Sets whether the model is filtered by map layer type.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:67
QgsDataItem * dataItem(const QModelIndex &index) const
Returns the data item at the specified proxy index, or nullptr if no item exists at the index...
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms...
Definition: qgsdataitem.h:243
QgsMimeDataUtils::Uri uri() const
Returns the (possibly invalid) uri of the selected data source.
void setLayerTypeFilter(QgsMapLayerType layerType)
Sets layer type filter to layerType and activates the filtering.
virtual Capabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:283