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