QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 
35  QgsBrowserGuiModel *browserModel,
36  bool setFilterByLayerType,
37  QgsMapLayerType layerType,
38  QWidget *parent )
39  : QgsPanelWidget( parent )
40 {
41  if ( ! browserModel )
42  {
43  mBrowserModel = new QgsBrowserGuiModel( this );
44  mBrowserModel->initialize();
45  }
46  else
47  {
48  mBrowserModel = browserModel;
49  mBrowserModel->initialize();
50  }
51 
52  setupUi( this );
53 
54  mBrowserProxyModel.setBrowserModel( mBrowserModel );
55  mBrowserTreeView->setHeaderHidden( true );
56 
57  if ( setFilterByLayerType )
58  {
59  // This will also set the (proxy) model
60  setLayerTypeFilter( layerType );
61  }
62  else
63  {
64  mBrowserTreeView->setModel( &mBrowserProxyModel );
65  setValid( false );
66  }
67 
68  mBrowserTreeView->setBrowserModel( mBrowserModel );
69 
70  mWidgetFilter->hide();
71  mLeFilter->setPlaceholderText( tr( "Type here to filter visible items…" ) );
72  // icons from http://www.fatcow.com/free-icons License: CC Attribution 3.0
73 
74  QMenu *menu = new QMenu( this );
75  menu->setSeparatorsCollapsible( false );
76  mBtnFilterOptions->setMenu( menu );
77  QAction *action = new QAction( tr( "Case Sensitive" ), menu );
78  action->setData( "case" );
79  action->setCheckable( true );
80  action->setChecked( false );
81  connect( action, &QAction::toggled, this, &QgsDataSourceSelectWidget::setCaseSensitive );
82  menu->addAction( action );
83  QActionGroup *group = new QActionGroup( menu );
84  action = new QAction( tr( "Filter Pattern Syntax" ), group );
85  action->setSeparator( true );
86  menu->addAction( action );
87  action = new QAction( tr( "Normal" ), group );
88  action->setData( QgsBrowserProxyModel::Normal );
89  action->setCheckable( true );
90  action->setChecked( true );
91  menu->addAction( action );
92  action = new QAction( tr( "Wildcard(s)" ), group );
93  action->setData( QgsBrowserProxyModel::Wildcards );
94  action->setCheckable( true );
95  menu->addAction( action );
96  action = new QAction( tr( "Regular Expression" ), group );
97  action->setData( QgsBrowserProxyModel::RegularExpression );
98  action->setCheckable( true );
99  menu->addAction( action );
100 
101  connect( mActionRefresh, &QAction::triggered, this, [ = ] { refreshModel( QModelIndex() ); } );
102  connect( mBrowserTreeView, &QgsBrowserTreeView::clicked, this, &QgsDataSourceSelectWidget::onLayerSelected );
103  connect( mBrowserTreeView, &QgsBrowserTreeView::doubleClicked, this, &QgsDataSourceSelectWidget::itemDoubleClicked );
104  connect( mActionCollapse, &QAction::triggered, mBrowserTreeView, &QgsBrowserTreeView::collapseAll );
105  connect( mActionShowFilter, &QAction::triggered, this, &QgsDataSourceSelectWidget::showFilterWidget );
106  connect( mLeFilter, &QgsFilterLineEdit::returnPressed, this, &QgsDataSourceSelectWidget::setFilter );
108  connect( mLeFilter, &QgsFilterLineEdit::textChanged, this, &QgsDataSourceSelectWidget::setFilter );
109  connect( group, &QActionGroup::triggered, this, &QgsDataSourceSelectWidget::setFilterSyntax );
110 
111  mBrowserToolbar->setIconSize( QgsGuiUtils::iconSize( true ) );
112 
113  if ( QgsSettings().value( QStringLiteral( "datasourceSelectFilterVisible" ), false, QgsSettings::Section::Gui ).toBool() )
114  {
115  mActionShowFilter->trigger();
116  }
117 }
118 
120 
122 {
123  QgsPanelWidget::showEvent( e );
124  QString lastSelectedPath( QgsSettings().value( QStringLiteral( "datasourceSelectLastSelectedItem" ),
125  QString(), QgsSettings::Section::Gui ).toString() );
126  if ( ! lastSelectedPath.isEmpty() )
127  {
128  QModelIndexList items = mBrowserProxyModel.match(
129  mBrowserProxyModel.index( 0, 0 ),
131  QVariant::fromValue( lastSelectedPath ),
132  1,
133  Qt::MatchRecursive );
134  if ( items.count( ) > 0 )
135  {
136  QModelIndex expandIndex = items.at( 0 );
137  if ( expandIndex.isValid() )
138  {
139  mBrowserTreeView->scrollTo( expandIndex, QgsBrowserTreeView::ScrollHint::PositionAtTop );
140  mBrowserTreeView->expand( expandIndex );
141  }
142  }
143  }
144 }
145 
147 {
148  QgsSettings().setValue( QStringLiteral( "datasourceSelectFilterVisible" ), visible, QgsSettings::Section::Gui );
149  mWidgetFilter->setVisible( visible );
150  if ( ! visible )
151  {
152  mLeFilter->setText( QString() );
153  setFilter();
154  }
155  else
156  {
157  mLeFilter->setFocus();
158  }
159 }
160 
161 void QgsDataSourceSelectWidget::setDescription( const QString &description )
162 {
163  if ( !description.isEmpty() )
164  {
165  if ( !mDescriptionLabel )
166  {
167  mDescriptionLabel = new QLabel();
168  mDescriptionLabel->setWordWrap( true );
169  mDescriptionLabel->setMargin( 4 );
170  mDescriptionLabel->setTextInteractionFlags( Qt::TextBrowserInteraction );
171  connect( mDescriptionLabel, &QLabel::linkActivated, this, [ = ]( const QString & link )
172  {
173  QUrl url( link );
174  QFileInfo file( url.toLocalFile() );
175  if ( file.exists() && !file.isDir() )
176  QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
177  else
178  QDesktopServices::openUrl( url );
179  } );
180  verticalLayout->insertWidget( 1, mDescriptionLabel );
181  }
182  mDescriptionLabel->setText( description );
183  }
184  else
185  {
186  if ( mDescriptionLabel )
187  {
188  verticalLayout->removeWidget( mDescriptionLabel );
189  delete mDescriptionLabel;
190  mDescriptionLabel = nullptr;
191  }
192  }
193 }
194 
196 {
197  QString filter = mLeFilter->text();
198  mBrowserProxyModel.setFilterString( filter );
199 }
200 
201 
202 void QgsDataSourceSelectWidget::refreshModel( const QModelIndex &index )
203 {
204 
205  QgsDataItem *item = mBrowserModel->dataItem( index );
206  if ( item )
207  {
208  QgsDebugMsgLevel( "path = " + item->path(), 2 );
209  }
210  else
211  {
212  QgsDebugMsg( QStringLiteral( "invalid item" ) );
213  }
214 
215  if ( item && ( item->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
216  {
217  mBrowserModel->refresh( index );
218  }
219 
220  for ( int i = 0; i < mBrowserModel->rowCount( index ); i++ )
221  {
222  QModelIndex idx = mBrowserModel->index( i, 0, index );
223  QModelIndex proxyIdx = mBrowserProxyModel.mapFromSource( idx );
224  QgsDataItem *child = mBrowserModel->dataItem( idx );
225 
226  // Check also expanded descendants so that the whole expanded path does not get collapsed if one item is collapsed.
227  // Fast items (usually root items) are refreshed so that when collapsed, it is obvious they are if empty (no expand symbol).
228  if ( mBrowserTreeView->isExpanded( proxyIdx ) || mBrowserTreeView->hasExpandedDescendant( proxyIdx ) || ( child && child->capabilities2() & Qgis::BrowserItemCapability::Fast ) )
229  {
230  refreshModel( idx );
231  }
232  else
233  {
234  if ( child && ( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
235  {
236  child->depopulate();
237  }
238  }
239  }
240 }
241 
242 void QgsDataSourceSelectWidget::setValid( bool valid )
243 {
244  const bool prev = mIsValid;
245  mIsValid = valid;
246  if ( prev != mIsValid )
247  emit validationChanged( mIsValid );
248 
249 }
250 
251 
253 {
254  if ( !action )
255  return;
256  mBrowserProxyModel.setFilterSyntax( static_cast< QgsBrowserProxyModel::FilterSyntax >( action->data().toInt() ) );
257 }
258 
260 {
261  mBrowserProxyModel.setFilterCaseSensitivity( caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
262 }
263 
265 {
266  mBrowserProxyModel.setFilterByLayerType( true );
267  mBrowserProxyModel.setLayerType( layerType );
268  // reset model and button
269  mBrowserTreeView->setModel( &mBrowserProxyModel );
270  setValid( false );
271 }
272 
274 {
275  return mUri;
276 }
277 
278 void QgsDataSourceSelectWidget::onLayerSelected( const QModelIndex &index )
279 {
280  bool isLayerCompatible = false;
281  mUri = QgsMimeDataUtils::Uri();
282  if ( index.isValid() )
283  {
284  const QgsDataItem *dataItem( mBrowserProxyModel.dataItem( index ) );
285  if ( dataItem )
286  {
287  const QgsLayerItem *layerItem = qobject_cast<const QgsLayerItem *>( dataItem );
288  if ( layerItem && ( ! mBrowserProxyModel.filterByLayerType() ||
289  ( layerItem->mapLayerType() == mBrowserProxyModel.layerType() ) ) )
290  {
291  isLayerCompatible = true;
292  mUri = layerItem->mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : layerItem->mimeUris().first();
293  // Store last viewed item
294  QgsSettings().setValue( QStringLiteral( "datasourceSelectLastSelectedItem" ), mBrowserProxyModel.data( index, QgsBrowserGuiModel::PathRole ).toString(), QgsSettings::Section::Gui );
295  }
296  }
297  }
298  setValid( isLayerCompatible );
299  emit selectionChanged();
300 }
301 
302 void QgsDataSourceSelectWidget::itemDoubleClicked( const QModelIndex &index )
303 {
304  onLayerSelected( index );
305  if ( mIsValid )
306  emit itemTriggered( uri() );
307 }
308 
309 //
310 // QgsDataSourceSelectDialog
311 //
312 
313 QgsDataSourceSelectDialog::QgsDataSourceSelectDialog( QgsBrowserGuiModel *browserModel, bool setFilterByLayerType, QgsMapLayerType layerType, QWidget *parent )
314  : QDialog( parent )
315 {
316  setWindowTitle( tr( "Select a Data Source" ) );
317  setObjectName( QStringLiteral( "QgsDataSourceSelectDialog" ) );
319 
320  mWidget = new QgsDataSourceSelectWidget( browserModel, setFilterByLayerType, layerType );
321 
322  QVBoxLayout *vl = new QVBoxLayout();
323  vl->addWidget( mWidget, 1 );
324  vl->setContentsMargins( 4, 4, 4, 4 );
325  QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
326  connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
327  connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
328  buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
329  connect( mWidget, &QgsDataSourceSelectWidget::validationChanged, buttonBox->button( QDialogButtonBox::Ok ), &QWidget::setEnabled );
330  connect( mWidget, &QgsDataSourceSelectWidget::itemTriggered, this, &QDialog::accept );
331 
332  // pressing escape should reject the dialog
333  connect( mWidget, &QgsPanelWidget::panelAccepted, this, &QDialog::reject );
334 
335  vl->addWidget( buttonBox );
336  setLayout( vl );
337 }
338 
340 {
341  mWidget->setLayerTypeFilter( layerType );
342 }
343 
344 void QgsDataSourceSelectDialog::setDescription( const QString &description )
345 {
346  mWidget->setDescription( description );
347 }
348 
350 {
351  return mWidget->uri();
352 }
353 
355 {
356  mWidget->showFilterWidget( visible );
357 }
358 
360 {
361  mWidget->setFilterSyntax( syntax );
362 }
363 
365 {
366  mWidget->setCaseSensitive( caseSensitive );
367 }
368 
370 {
371  mWidget->setFilter();
372 
373 }
@ 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:346
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:295
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:65
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:156
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS's underlying public inte...
Definition: qgsgui.cpp:71
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:46
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