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