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