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