QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsbrowserdockwidget_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsbrowserdockwidget_p.cpp
3 
4  Private classes for QgsBrowserDockWidget
5 
6  ---------------------
7  begin : May 2017
8  copyright : (C) 2017 by Alessandro Pasotti
9  real work done by : (C) 2011 by Martin Dobias
10  email : a dot pasotti at itopen dot it
11  ---------------------
12  ***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  ***************************************************************************/
20 #include "qgsbrowserdockwidget_p.h"
21 
22 #include <memory>
23 
24 #include <QAbstractTextDocumentLayout>
25 #include <QHeaderView>
26 #include <QTreeView>
27 #include <QMenu>
28 #include <QToolButton>
29 #include <QFileDialog>
30 #include <QPlainTextDocumentLayout>
31 #include <QSortFilterProxyModel>
32 
33 #include "qgsbrowsermodel.h"
34 #include "qgsbrowsertreeview.h"
35 #include "qgslogger.h"
36 #include "qgsrasterlayer.h"
37 #include "qgsvectorlayer.h"
38 #include "qgsproject.h"
39 #include "qgssettings.h"
40 #include "qgsmeshlayer.h"
41 
42 #include <QDragEnterEvent>
43 
44 
46 
47 
48 QgsBrowserPropertiesWrapLabel::QgsBrowserPropertiesWrapLabel( const QString &text, QWidget *parent )
49  : QTextEdit( text, parent )
50 {
51  setReadOnly( true );
52  setFrameStyle( QFrame::NoFrame );
53  setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
54  QPalette pal = palette();
55  pal.setColor( QPalette::Base, Qt::transparent );
56  setPalette( pal );
57  setLineWrapMode( QTextEdit::WidgetWidth );
58  setWordWrapMode( QTextOption::WrapAnywhere );
59  connect( qobject_cast<QAbstractTextDocumentLayout *>( document()->documentLayout() ), &QAbstractTextDocumentLayout::documentSizeChanged,
60  this, &QgsBrowserPropertiesWrapLabel::adjustHeight );
61  setMaximumHeight( 20 );
62 }
63 
64 void QgsBrowserPropertiesWrapLabel::adjustHeight( QSizeF size )
65 {
66  int height = size.height() + 2 * frameWidth();
67  setMinimumHeight( height );
68  setMaximumHeight( height );
69 }
70 
71 QgsBrowserPropertiesWidget::QgsBrowserPropertiesWidget( QWidget *parent )
72  : QWidget( parent )
73 {
74 }
75 
76 void QgsBrowserPropertiesWidget::setWidget( QWidget *paramWidget )
77 {
78  QVBoxLayout *layout = new QVBoxLayout( this );
79  paramWidget->setParent( this );
80  layout->addWidget( paramWidget );
81 }
82 
83 QgsBrowserPropertiesWidget *QgsBrowserPropertiesWidget::createWidget( QgsDataItem *item, QWidget *parent )
84 {
85  QgsBrowserPropertiesWidget *propertiesWidget = nullptr;
86  // In general, we would like to show all items' paramWidget, but top level items like
87  // WMS etc. have currently too large widgets which do not fit well to browser properties widget
88  if ( item->type() == QgsDataItem::Directory )
89  {
90  propertiesWidget = new QgsBrowserDirectoryProperties( parent );
91  propertiesWidget->setItem( item );
92  }
93  else if ( item->type() == QgsDataItem::Layer )
94  {
95  // prefer item's widget over standard layer widget
96  QWidget *paramWidget = item->paramWidget();
97  if ( paramWidget )
98  {
99  propertiesWidget = new QgsBrowserPropertiesWidget( parent );
100  propertiesWidget->setWidget( paramWidget );
101  }
102  else
103  {
104  propertiesWidget = new QgsBrowserLayerProperties( parent );
105  propertiesWidget->setItem( item );
106  }
107  }
108  return propertiesWidget;
109 }
110 
111 QgsBrowserLayerProperties::QgsBrowserLayerProperties( QWidget *parent )
112  : QgsBrowserPropertiesWidget( parent )
113 {
114  setupUi( this );
115 
116  mUriLabel = new QgsBrowserPropertiesWrapLabel( QString(), this );
117  mHeaderGridLayout->addItem( new QWidgetItem( mUriLabel ), 1, 1 );
118 }
119 
120 void QgsBrowserLayerProperties::setItem( QgsDataItem *item )
121 {
122  QgsLayerItem *layerItem = qobject_cast<QgsLayerItem *>( item );
123  if ( !layerItem )
124  return;
125 
126  mNoticeLabel->clear();
127 
128  QgsMapLayer::LayerType type = layerItem->mapLayerType();
129  QString layerMetadata = tr( "Error" );
131 
132  // temporarily override /Projections/defaultBehavior to avoid dialog prompt
133  QgsSettings settings;
134  QString defaultProjectionOption = settings.value( QStringLiteral( "Projections/defaultBehavior" ), "prompt" ).toString();
135  if ( settings.value( QStringLiteral( "Projections/defaultBehavior" ), "prompt" ).toString() == QLatin1String( "prompt" ) )
136  {
137  settings.setValue( QStringLiteral( "Projections/defaultBehavior" ), "useProject" );
138  }
139 
140  // find root item
141  // we need to create a temporary layer to get metadata
142  // we could use a provider but the metadata is not as complete and "pretty" and this is easier
143  QgsDebugMsg( QString( "creating temporary layer using path %1" ).arg( layerItem->path() ) );
144  if ( type == QgsMapLayer::RasterLayer )
145  {
146  QgsDebugMsg( "creating raster layer" );
147  // should copy code from addLayer() to split uri ?
148  std::unique_ptr<QgsRasterLayer> layer( new QgsRasterLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ) );
149  if ( layer )
150  {
151  if ( layer->isValid() )
152  {
153  layerCrs = layer->crs();
154  layerMetadata = layer->htmlMetadata();
155  }
156  }
157  }
158  else if ( type == QgsMapLayer::MeshLayer )
159  {
160  QgsDebugMsg( "creating mesh layer" );
161  std::unique_ptr<QgsMeshLayer> layer( new QgsMeshLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ) );
162  if ( layer )
163  {
164  if ( layer->isValid() )
165  {
166  layerCrs = layer->crs();
167  layerMetadata = layer->htmlMetadata();
168  }
169  }
170  }
171  else if ( type == QgsMapLayer::VectorLayer )
172  {
173  QgsDebugMsg( "creating vector layer" );
174  std::unique_ptr<QgsVectorLayer> layer( new QgsVectorLayer( layerItem->uri(), layerItem->name(), layerItem->providerKey() ) );
175  if ( layer )
176  {
177  if ( layer->isValid() )
178  {
179  layerCrs = layer->crs();
180  layerMetadata = layer->htmlMetadata();
181  }
182  }
183  }
184  else if ( type == QgsMapLayer::PluginLayer )
185  {
186  // TODO: support display of properties for plugin layers
187  return;
188  }
189 
190  // restore /Projections/defaultBehavior
191  if ( defaultProjectionOption == QLatin1String( "prompt" ) )
192  {
193  settings.setValue( QStringLiteral( "Projections/defaultBehavior" ), defaultProjectionOption );
194  }
195 
196  mNameLabel->setText( layerItem->name() );
197  mUriLabel->setText( layerItem->uri() );
198  mProviderLabel->setText( layerItem->providerKey() );
199  QString myStyle = QgsApplication::reportStyleSheet();
200  mMetadataTextBrowser->document()->setDefaultStyleSheet( myStyle );
201  mMetadataTextBrowser->setHtml( layerMetadata );
202 
203  // report if layer was set to to project crs without prompt (may give a false positive)
204  if ( defaultProjectionOption == QLatin1String( "prompt" ) )
205  {
206  QgsCoordinateReferenceSystem defaultCrs =
208  if ( layerCrs == defaultCrs )
209  mNoticeLabel->setText( "NOTICE: Layer srs set from project (" + defaultCrs.authid() + ')' );
210  }
211 
212  if ( mNoticeLabel->text().isEmpty() )
213  {
214  mNoticeLabel->hide();
215  }
216 }
217 
218 void QgsBrowserLayerProperties::setCondensedMode( bool condensedMode )
219 {
220  if ( condensedMode )
221  {
222  mUriLabel->setLineWrapMode( QTextEdit::NoWrap );
223  mUriLabel->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
224  mUriLabel->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
225  }
226  else
227  {
228  mUriLabel->setLineWrapMode( QTextEdit::WidgetWidth );
229  mUriLabel->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
230  mUriLabel->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
231  }
232 }
233 
234 QgsBrowserDirectoryProperties::QgsBrowserDirectoryProperties( QWidget *parent )
235  : QgsBrowserPropertiesWidget( parent )
236 
237 {
238  setupUi( this );
239 
240  mPathLabel = new QgsBrowserPropertiesWrapLabel( QString(), mHeaderWidget );
241  mHeaderGridLayout->addItem( new QWidgetItem( mPathLabel ), 0, 1 );
242 }
243 
244 void QgsBrowserDirectoryProperties::setItem( QgsDataItem *item )
245 {
246  QgsDirectoryItem *directoryItem = qobject_cast<QgsDirectoryItem *>( item );
247  if ( !item )
248  return;
249 
250  mPathLabel->setText( QDir::toNativeSeparators( directoryItem->dirPath() ) );
251  mDirectoryWidget = new QgsDirectoryParamWidget( directoryItem->dirPath(), this );
252  mLayout->addWidget( mDirectoryWidget );
253 }
254 
255 QgsBrowserPropertiesDialog::QgsBrowserPropertiesDialog( const QString &settingsSection, QWidget *parent )
256  : QDialog( parent )
257  , mSettingsSection( settingsSection )
258 {
259  setupUi( this );
260  QgsSettings settings;
261  restoreGeometry( settings.value( mSettingsSection + "/propertiesDialog/geometry" ).toByteArray() );
262 }
263 
264 QgsBrowserPropertiesDialog::~QgsBrowserPropertiesDialog()
265 {
266  QgsSettings settings;
267  settings.setValue( mSettingsSection + "/propertiesDialog/geometry", saveGeometry() );
268 }
269 
270 void QgsBrowserPropertiesDialog::setItem( QgsDataItem *item )
271 {
272  if ( !item )
273  return;
274 
275  mPropertiesWidget = QgsBrowserPropertiesWidget::createWidget( item, this );
276  mLayout->addWidget( mPropertiesWidget );
277  setWindowTitle( item->type() == QgsDataItem::Layer ? tr( "Layer Properties" ) : tr( "Directory Properties" ) );
278 }
279 
280 
281 //
282 // QgsDockBrowserTreeView
283 //
284 
285 QgsDockBrowserTreeView::QgsDockBrowserTreeView( QWidget *parent ) : QgsBrowserTreeView( parent )
286 {
287  setDragDropMode( QTreeView::DragDrop ); // sets also acceptDrops + dragEnabled
288  setSelectionMode( QAbstractItemView::ExtendedSelection );
289  setContextMenuPolicy( Qt::CustomContextMenu );
290  setHeaderHidden( true );
291  setDropIndicatorShown( true );
292 
293 }
294 
295 void QgsDockBrowserTreeView::setAction( QDropEvent *e )
296 {
297  // if this mime data come from layer tree, the proposed action will be MoveAction
298  // but for browser we really need CopyAction
299  if ( e->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) &&
300  e->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
301  {
302  e->setDropAction( Qt::CopyAction );
303  }
304 }
305 
306 void QgsDockBrowserTreeView::dragEnterEvent( QDragEnterEvent *e )
307 {
308  setAction( e );
309 
310  // accept drag enter so that our widget will not get ignored
311  // and drag events will not get passed to QgisApp
312  e->accept();
313 }
314 
315 void QgsDockBrowserTreeView::dragMoveEvent( QDragMoveEvent *e )
316 {
317  // do not accept drops above/below items
318  /*if ( dropIndicatorPosition() != QAbstractItemView::OnItem )
319  {
320  QgsDebugMsg("drag not on item");
321  e->ignore();
322  return;
323  }*/
324 
325  setAction( e );
326  QTreeView::dragMoveEvent( e );
327  // reset action because QTreeView::dragMoveEvent() accepts proposed action
328  setAction( e );
329 
330  if ( !e->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
331  {
332  e->ignore();
333  return;
334  }
335 }
336 
337 void QgsDockBrowserTreeView::dropEvent( QDropEvent *e )
338 {
339  setAction( e );
340  QTreeView::dropEvent( e );
341  // reset action because QTreeView::dropEvent() accepts proposed action
342  setAction( e );
343 }
344 
345 //
346 // QgsBrowserTreeFilterProxyModel
347 //
348 
349 QgsBrowserTreeFilterProxyModel::QgsBrowserTreeFilterProxyModel( QObject *parent )
350  : QSortFilterProxyModel( parent )
351  , mPatternSyntax( QStringLiteral( "normal" ) )
352  , mCaseSensitivity( Qt::CaseInsensitive )
353 {
354  setDynamicSortFilter( true );
355  setSortRole( QgsBrowserModel::SortRole );
356  setSortCaseSensitivity( Qt::CaseInsensitive );
357  sort( 0 );
358 }
359 
360 void QgsBrowserTreeFilterProxyModel::setBrowserModel( QgsBrowserModel *model )
361 {
362  mModel = model;
363  setSourceModel( model );
364 }
365 
366 void QgsBrowserTreeFilterProxyModel::setFilterSyntax( const QString &syntax )
367 {
368  QgsDebugMsg( QString( "syntax = %1" ).arg( syntax ) );
369  if ( mPatternSyntax == syntax )
370  return;
371  mPatternSyntax = syntax;
372  updateFilter();
373 }
374 
375 void QgsBrowserTreeFilterProxyModel::setFilter( const QString &filter )
376 {
377  QgsDebugMsg( QString( "filter = %1" ).arg( mFilter ) );
378  if ( mFilter == filter )
379  return;
380  mFilter = filter;
381  updateFilter();
382 }
383 
384 void QgsBrowserTreeFilterProxyModel::setCaseSensitive( bool caseSensitive )
385 {
386  mCaseSensitivity = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
387  updateFilter();
388 }
389 
390 void QgsBrowserTreeFilterProxyModel::updateFilter()
391 {
392  QgsDebugMsg( QString( "filter = %1 syntax = %2" ).arg( mFilter, mPatternSyntax ) );
393  mREList.clear();
394  if ( mPatternSyntax == QLatin1String( "normal" ) )
395  {
396  Q_FOREACH ( const QString &f, mFilter.split( '|' ) )
397  {
398  QRegExp rx( QString( "*%1*" ).arg( f.trimmed() ) );
399  rx.setPatternSyntax( QRegExp::Wildcard );
400  rx.setCaseSensitivity( mCaseSensitivity );
401  mREList.append( rx );
402  }
403  }
404  else if ( mPatternSyntax == QLatin1String( "wildcard" ) )
405  {
406  Q_FOREACH ( const QString &f, mFilter.split( '|' ) )
407  {
408  QRegExp rx( f.trimmed() );
409  rx.setPatternSyntax( QRegExp::Wildcard );
410  rx.setCaseSensitivity( mCaseSensitivity );
411  mREList.append( rx );
412  }
413  }
414  else
415  {
416  QRegExp rx( mFilter.trimmed() );
417  rx.setPatternSyntax( QRegExp::RegExp );
418  rx.setCaseSensitivity( mCaseSensitivity );
419  mREList.append( rx );
420  }
421  invalidateFilter();
422 }
423 
424 bool QgsBrowserTreeFilterProxyModel::filterAcceptsString( const QString &value ) const
425 {
426  if ( mPatternSyntax == QLatin1String( "normal" ) || mPatternSyntax == QLatin1String( "wildcard" ) )
427  {
428  Q_FOREACH ( const QRegExp &rx, mREList )
429  {
430  QgsDebugMsg( QString( "value: [%1] rx: [%2] match: %3" ).arg( value, rx.pattern() ).arg( rx.exactMatch( value ) ) );
431  if ( rx.exactMatch( value ) )
432  return true;
433  }
434  }
435  else
436  {
437  Q_FOREACH ( const QRegExp &rx, mREList )
438  {
439  QgsDebugMsg( QString( "value: [%1] rx: [%2] match: %3" ).arg( value, rx.pattern() ).arg( rx.indexIn( value ) ) );
440  if ( rx.indexIn( value ) != -1 )
441  return true;
442  }
443  }
444  return false;
445 }
446 
447 bool QgsBrowserTreeFilterProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
448 {
449  if ( mFilter.isEmpty() || !mModel )
450  return true;
451 
452  QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
453  return filterAcceptsItem( sourceIndex ) || filterAcceptsAncestor( sourceIndex ) || filterAcceptsDescendant( sourceIndex );
454 }
455 
456 bool QgsBrowserTreeFilterProxyModel::filterAcceptsAncestor( const QModelIndex &sourceIndex ) const
457 {
458  if ( !mModel )
459  return true;
460 
461  QModelIndex sourceParentIndex = mModel->parent( sourceIndex );
462  if ( !sourceParentIndex.isValid() )
463  return false;
464  if ( filterAcceptsItem( sourceParentIndex ) )
465  return true;
466 
467  return filterAcceptsAncestor( sourceParentIndex );
468 }
469 
470 bool QgsBrowserTreeFilterProxyModel::filterAcceptsDescendant( const QModelIndex &sourceIndex ) const
471 {
472  if ( !mModel )
473  return true;
474 
475  for ( int i = 0; i < mModel->rowCount( sourceIndex ); i++ )
476  {
477  QgsDebugMsg( QString( "i = %1" ).arg( i ) );
478  QModelIndex sourceChildIndex = mModel->index( i, 0, sourceIndex );
479  if ( filterAcceptsItem( sourceChildIndex ) )
480  return true;
481  if ( filterAcceptsDescendant( sourceChildIndex ) )
482  return true;
483  }
484  return false;
485 }
486 
487 bool QgsBrowserTreeFilterProxyModel::filterAcceptsItem( const QModelIndex &sourceIndex ) const
488 {
489  if ( !mModel )
490  return true;
491  //accept item if either displayed text or comment role matches string
492  QString comment = mModel->data( sourceIndex, QgsBrowserModel::CommentRole ).toString();
493  return ( filterAcceptsString( mModel->data( sourceIndex, Qt::DisplayRole ).toString() )
494  || ( !comment.isEmpty() && filterAcceptsString( comment ) ) );
495 }
496 
QString path() const
Definition: qgsdataitem.h:266
The QgsBrowserTreeView class extends QTreeView with save/restore tree state functionality.
QString providerKey() const
Returns provider key.
Definition: qgsdataitem.h:450
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:257
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsMapLayer::LayerType mapLayerType() const
Returns QgsMapLayer::LayerType.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
Type type() const
Definition: qgsdataitem.h:238
static QString reportStyleSheet()
Returns a standard css style sheet for reports.
virtual QWidget * paramWidget()
Definition: qgsdataitem.h:147
void saveGeometry(QWidget *widget, const QString &keyName)
Save the wigget geometry into settings.
Added in 3.2.
Definition: qgsmaplayer.h:107
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QModelIndex parent(const QModelIndex &index) const override
LayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:102
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:91
A directory: contains subdirectories and layers.
Definition: qgsdataitem.h:531
Base class for all items in the model.
Definition: qgsdataitem.h:49
Custom sort role, see QgsDataItem::sortKey()
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:391
This class represents a coordinate reference system (CRS).
QString uri() const
Returns layer uri or empty string if layer cannot be created.
Definition: qgsdataitem.h:447
QString dirPath() const
Definition: qgsdataitem.h:560
A model for showing available data sources and other items in a structured tree.
Item that represents a layer that can be opened with one of the providers.
Definition: qgsdataitem.h:409
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:90
Represents a vector layer which manages a vector based data sets.
QString authid() const
Returns the authority identifier for the CRS.