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