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