QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 #include "qgsgui.h"
42 #include "qgsnative.h"
43 #include "qgsmaptoolpan.h"
44 #include "qgsvectorlayercache.h"
45 #include "qgsattributetablemodel.h"
47 #include <QDesktopServices>
48 
49 #include <QDragEnterEvent>
50 
51 
53 
54 
55 QgsBrowserPropertiesWrapLabel::QgsBrowserPropertiesWrapLabel( const QString &text, QWidget *parent )
56  : QTextEdit( text, parent )
57 {
58  setReadOnly( true );
59  setFrameStyle( QFrame::NoFrame );
60  setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum );
61  QPalette pal = palette();
62  pal.setColor( QPalette::Base, Qt::transparent );
63  setPalette( pal );
64  setLineWrapMode( QTextEdit::WidgetWidth );
65  setWordWrapMode( QTextOption::WrapAnywhere );
66  connect( document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged,
67  this, &QgsBrowserPropertiesWrapLabel::adjustHeight );
68  setMaximumHeight( 20 );
69 }
70 
71 void QgsBrowserPropertiesWrapLabel::adjustHeight( QSizeF size )
72 {
73  int height = size.height() + 2 * frameWidth();
74  setMinimumHeight( height );
75  setMaximumHeight( height );
76 }
77 
78 QgsBrowserPropertiesWidget::QgsBrowserPropertiesWidget( QWidget *parent )
79  : QWidget( parent )
80 {
81 }
82 
83 void QgsBrowserPropertiesWidget::setWidget( QWidget *paramWidget )
84 {
85  QVBoxLayout *layout = new QVBoxLayout( this );
86  paramWidget->setParent( this );
87  layout->addWidget( paramWidget );
88 }
89 
90 QgsBrowserPropertiesWidget *QgsBrowserPropertiesWidget::createWidget( QgsDataItem *item, QWidget *parent )
91 {
92  QgsBrowserPropertiesWidget *propertiesWidget = nullptr;
93  // In general, we would like to show all items' paramWidget, but top level items like
94  // WMS etc. have currently too large widgets which do not fit well to browser properties widget
95  if ( item->type() == QgsDataItem::Directory )
96  {
97  propertiesWidget = new QgsBrowserDirectoryProperties( parent );
98  propertiesWidget->setItem( item );
99  }
100  else if ( item->type() == QgsDataItem::Layer )
101  {
102  // prefer item's widget over standard layer widget
103  QWidget *paramWidget = item->paramWidget();
104  if ( paramWidget )
105  {
106  propertiesWidget = new QgsBrowserPropertiesWidget( parent );
107  propertiesWidget->setWidget( paramWidget );
108  }
109  else
110  {
111  propertiesWidget = new QgsBrowserLayerProperties( parent );
112  propertiesWidget->setItem( item );
113  }
114  }
115  return propertiesWidget;
116 }
117 
118 QgsBrowserLayerProperties::QgsBrowserLayerProperties( QWidget *parent )
119  : QgsBrowserPropertiesWidget( parent )
120 {
121  setupUi( this );
122 
123  // we don't want links to open in the little widget, open them externally instead
124  mMetadataTextBrowser->setOpenLinks( false );
125  connect( mMetadataTextBrowser, &QTextBrowser::anchorClicked, this, &QgsBrowserLayerProperties::urlClicked );
126 
127  mMapCanvas->setProperty( "browser_canvas", true );
128  mMapCanvas->setLayers( QList< QgsMapLayer * >() );
129  mMapCanvas->setMapTool( new QgsMapToolPan( mMapCanvas ) );
130  mMapCanvas->freeze( true );
131 
132  connect( mTabWidget, &QTabWidget::currentChanged, this, [ = ]
133  {
134  if ( mTabWidget->currentWidget() == mPreviewTab && mMapCanvas->isFrozen() )
135  {
136  mMapCanvas->freeze( false );
137  mMapCanvas->refresh();
138  }
139  else if ( mTabWidget->currentWidget() == mAttributesTab )
140  {
141  if ( ! mAttributeTableFilterModel )
142  loadAttributeTable();
143  }
144  } );
145 }
146 
147 class ProjectionSettingRestorer
148 {
149  public:
150 
151  ProjectionSettingRestorer()
152  {
153  QgsSettings settings;
154  previousSetting = settings.value( QStringLiteral( "/Projections/defaultBehavior" ) ).toString();
155  settings.setValue( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "useProject" ) );
156  }
157 
158  ~ProjectionSettingRestorer()
159  {
160  QgsSettings settings;
161  settings.setValue( QStringLiteral( "/Projections/defaultBehavior" ), previousSetting );
162  }
163 
164  QString previousSetting;
165 };
166 
167 void QgsBrowserLayerProperties::setItem( QgsDataItem *item )
168 {
169  QgsLayerItem *layerItem = qobject_cast<QgsLayerItem *>( item );
170  if ( !layerItem )
171  return;
172 
173  mNoticeLabel->clear();
174 
175  QgsMapLayer::LayerType type = layerItem->mapLayerType();
176  QString layerMetadata = tr( "Error" );
178 
179  QString defaultProjectionOption = QgsSettings().value( QStringLiteral( "Projections/defaultBehavior" ), "prompt" ).toString();
180  // temporarily override /Projections/defaultBehavior to avoid dialog prompt
181  // TODO - remove when there is a cleaner way to block the unknown projection dialog!
182  ProjectionSettingRestorer restorer;
183  ( void )restorer; // no warnings
184 
185  mLayer.reset();
186 
187  // find root item
188  // we need to create a temporary layer to get metadata
189  // we could use a provider but the metadata is not as complete and "pretty" and this is easier
190  QgsDebugMsg( QStringLiteral( "creating temporary layer using path %1" ).arg( layerItem->path() ) );
191  if ( type == QgsMapLayer::RasterLayer )
192  {
193  QgsDebugMsg( QStringLiteral( "creating raster layer" ) );
194  // should copy code from addLayer() to split uri ?
195  mLayer = qgis::make_unique< QgsRasterLayer >( layerItem->uri(), layerItem->uri(), layerItem->providerKey() );
196  }
197  else if ( type == QgsMapLayer::MeshLayer )
198  {
199  QgsDebugMsg( QStringLiteral( "creating mesh layer" ) );
200  mLayer = qgis::make_unique < QgsMeshLayer >( layerItem->uri(), layerItem->uri(), layerItem->providerKey() );
201  }
202  else if ( type == QgsMapLayer::VectorLayer )
203  {
204  QgsDebugMsg( QStringLiteral( "creating vector layer" ) );
205  mLayer = qgis::make_unique < QgsVectorLayer>( layerItem->uri(), layerItem->name(), layerItem->providerKey() );
206  }
207  else if ( type == QgsMapLayer::PluginLayer )
208  {
209  // TODO: support display of properties for plugin layers
210  return;
211  }
212 
213  mAttributeTable->setModel( nullptr );
214  if ( mAttributeTableFilterModel )
215  {
216  // Cleanup
217  mAttributeTableFilterModel->deleteLater();
218  mAttributeTableFilterModel = nullptr;
219  }
220  if ( mLayer && mLayer->isValid() )
221  {
222  bool ok = false;
223  mLayer->loadDefaultMetadata( ok );
224  layerCrs = mLayer->crs();
225  layerMetadata = mLayer->htmlMetadata();
226 
227  mMapCanvas->setDestinationCrs( mLayer->crs() );
228  mMapCanvas->setLayers( QList< QgsMapLayer * >() << mLayer.get() );
229  mMapCanvas->zoomToFullExtent();
230 
231  if ( mAttributesTab && mLayer->type() != QgsMapLayer::VectorLayer )
232  {
233  mTabWidget->removeTab( mTabWidget->indexOf( mAttributesTab ) );
234  mAttributesTab = nullptr;
235  }
236  }
237 
238  QString myStyle = QgsApplication::reportStyleSheet();
239  mMetadataTextBrowser->document()->setDefaultStyleSheet( myStyle );
240  mMetadataTextBrowser->setHtml( layerMetadata );
241 
242  // report if layer was set to to project crs without prompt (may give a false positive)
243  if ( defaultProjectionOption == QLatin1String( "prompt" ) )
244  {
245  QgsCoordinateReferenceSystem defaultCrs =
247  if ( layerCrs == defaultCrs )
248  mNoticeLabel->setText( "NOTICE: Layer CRS set from project (" + defaultCrs.authid() + ')' );
249  }
250 
251  if ( mNoticeLabel->text().isEmpty() )
252  {
253  mNoticeLabel->hide();
254  }
255 }
256 
257 void QgsBrowserLayerProperties::setCondensedMode( bool )
258 {
259 
260 }
261 
262 void QgsBrowserLayerProperties::urlClicked( const QUrl &url )
263 {
264  QFileInfo file( url.toLocalFile() );
265  if ( file.exists() && !file.isDir() )
266  QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
267  else
268  QDesktopServices::openUrl( url );
269 }
270 
271 void QgsBrowserLayerProperties::loadAttributeTable()
272 {
273  if ( !mLayer || !mLayer->isValid() || mLayer->type() != QgsMapLayer::VectorLayer )
274  return;
275 
276  // Initialize the cache
277  QgsVectorLayerCache *layerCache = new QgsVectorLayerCache( qobject_cast< QgsVectorLayer * >( mLayer.get() ), 1000, this );
278  layerCache->setCacheGeometry( false );
279  QgsAttributeTableModel *tableModel = new QgsAttributeTableModel( layerCache, this );
280  mAttributeTableFilterModel = new QgsAttributeTableFilterModel( nullptr, tableModel, this );
281  tableModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setLimit( 100 ) );
282  layerCache->setParent( tableModel );
283  tableModel->setParent( mAttributeTableFilterModel );
284 
285  mAttributeTable->setModel( mAttributeTableFilterModel );
286  tableModel->loadLayer();
287  QFont font = mAttributeTable->font();
288  int fontSize = font.pointSize();
289 #ifdef Q_OS_WIN
290  fontSize = std::max( fontSize - 1, 8 ); // bit less on windows, due to poor rendering of small point sizes
291 #else
292  fontSize = std::max( fontSize - 2, 6 );
293 #endif
294  font.setPointSize( fontSize );
295  mAttributeTable->setFont( font );
296 
297  // we can safely do this expensive operation here (unlike in the main attribute table), because at most we have only 100 rows...
298  mAttributeTable->resizeColumnsToContents();
299  mAttributeTable->resizeRowsToContents();
300  mAttributeTable->verticalHeader()->setVisible( false ); // maximize valuable table space
301  mAttributeTable->setAlternatingRowColors( true );
302 }
303 
304 QgsBrowserDirectoryProperties::QgsBrowserDirectoryProperties( QWidget *parent )
305  : QgsBrowserPropertiesWidget( parent )
306 
307 {
308  setupUi( this );
309 
310  mPathLabel = new QgsBrowserPropertiesWrapLabel( QString(), mHeaderWidget );
311  mHeaderGridLayout->addItem( new QWidgetItem( mPathLabel ), 0, 1 );
312 }
313 
314 void QgsBrowserDirectoryProperties::setItem( QgsDataItem *item )
315 {
316  QgsDirectoryItem *directoryItem = qobject_cast<QgsDirectoryItem *>( item );
317  if ( !item )
318  return;
319 
320  mPathLabel->setText( QDir::toNativeSeparators( directoryItem->dirPath() ) );
321  mDirectoryWidget = new QgsDirectoryParamWidget( directoryItem->dirPath(), this );
322  mLayout->addWidget( mDirectoryWidget );
323 }
324 
325 QgsBrowserPropertiesDialog::QgsBrowserPropertiesDialog( const QString &settingsSection, QWidget *parent )
326  : QDialog( parent )
327  , mSettingsSection( settingsSection )
328 {
329  setupUi( this );
330  QgsSettings settings;
331  restoreGeometry( settings.value( mSettingsSection + "/propertiesDialog/geometry" ).toByteArray() );
332 }
333 
334 QgsBrowserPropertiesDialog::~QgsBrowserPropertiesDialog()
335 {
336  QgsSettings settings;
337  settings.setValue( mSettingsSection + "/propertiesDialog/geometry", saveGeometry() );
338 }
339 
340 void QgsBrowserPropertiesDialog::setItem( QgsDataItem *item )
341 {
342  if ( !item )
343  return;
344 
345  mPropertiesWidget = QgsBrowserPropertiesWidget::createWidget( item, this );
346  mLayout->addWidget( mPropertiesWidget );
347  setWindowTitle( item->type() == QgsDataItem::Layer ? tr( "Layer Properties" ) : tr( "Directory Properties" ) );
348 }
349 
350 
351 //
352 // QgsDockBrowserTreeView
353 //
354 
355 QgsDockBrowserTreeView::QgsDockBrowserTreeView( QWidget *parent ) : QgsBrowserTreeView( parent )
356 {
357  setDragDropMode( QTreeView::DragDrop ); // sets also acceptDrops + dragEnabled
358  setSelectionMode( QAbstractItemView::ExtendedSelection );
359  setContextMenuPolicy( Qt::CustomContextMenu );
360  setHeaderHidden( true );
361  setDropIndicatorShown( true );
362 
363 }
364 
365 void QgsDockBrowserTreeView::setAction( QDropEvent *e )
366 {
367  // if this mime data come from layer tree, the proposed action will be MoveAction
368  // but for browser we really need CopyAction
369  if ( e->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) &&
370  e->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
371  {
372  e->setDropAction( Qt::CopyAction );
373  }
374 }
375 
376 void QgsDockBrowserTreeView::dragEnterEvent( QDragEnterEvent *e )
377 {
378  setAction( e );
379 
380  // accept drag enter so that our widget will not get ignored
381  // and drag events will not get passed to QgisApp
382  e->accept();
383 }
384 
385 void QgsDockBrowserTreeView::dragMoveEvent( QDragMoveEvent *e )
386 {
387  // do not accept drops above/below items
388  /*if ( dropIndicatorPosition() != QAbstractItemView::OnItem )
389  {
390  QgsDebugMsg("drag not on item");
391  e->ignore();
392  return;
393  }*/
394 
395  setAction( e );
396  QTreeView::dragMoveEvent( e );
397  // reset action because QTreeView::dragMoveEvent() accepts proposed action
398  setAction( e );
399 
400  if ( !e->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
401  {
402  e->ignore();
403  return;
404  }
405 }
406 
407 void QgsDockBrowserTreeView::dropEvent( QDropEvent *e )
408 {
409  setAction( e );
410  QTreeView::dropEvent( e );
411  // reset action because QTreeView::dropEvent() accepts proposed action
412  setAction( e );
413 }
414 
415 
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
The QgsBrowserTreeView class extends QTreeView with save/restore tree state functionality.
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:283
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS&#39;s underlying public inte...
Definition: qgsgui.cpp:53
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
static QString reportStyleSheet()
Returns a standard css style sheet for reports.
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:47
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:110
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
QString dirPath() const
Definition: qgsdataitem.h:600
LayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:105
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:95
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QString path() const
Definition: qgsdataitem.h:292
Type type() const
Definition: qgsdataitem.h:264
QString providerKey() const
Returns provider key.
Definition: qgsdataitem.h:476
A directory: contains subdirectories and layers.
Definition: qgsdataitem.h:571
Base class for all items in the model.
Definition: qgsdataitem.h:49
QgsMapLayer::LayerType mapLayerType() const
Returns QgsMapLayer::LayerType.
This class caches features of a given QgsVectorLayer.
QString uri() const
Returns layer uri or empty string if layer cannot be created.
Definition: qgsdataitem.h:473
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void setCacheGeometry(bool cacheGeometry)
Enable or disable the caching of geometries.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:411
This class represents a coordinate reference system (CRS).
QString authid() const
Returns the authority identifier for the CRS.
Item that represents a layer that can be opened with one of the providers.
Definition: qgsdataitem.h:435
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A map tool for panning the map.
Definition: qgsmaptoolpan.h:29