QGIS API Documentation  3.6.0-Noosa (5873452)
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  QgsMapLayer::LayerType 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" ) );
205  mLayer = qgis::make_unique < QgsMeshLayer >( layerItem->uri(), layerItem->name(), layerItem->providerKey() );
206  break;
207  }
208 
210  {
211  QgsDebugMsg( QStringLiteral( "creating vector layer" ) );
212  mLayer = qgis::make_unique < QgsVectorLayer>( layerItem->uri(), layerItem->name(), layerItem->providerKey() );
213  break;
214  }
215 
217  {
218  // TODO: support display of properties for plugin layers
219  return;
220  }
221  }
222 
223  mAttributeTable->setModel( nullptr );
224  if ( mAttributeTableFilterModel )
225  {
226  // Cleanup
227  mAttributeTableFilterModel->deleteLater();
228  mAttributeTableFilterModel = nullptr;
229  }
230  if ( mLayer && mLayer->isValid() )
231  {
232  bool ok = false;
233  mLayer->loadDefaultMetadata( ok );
234  layerCrs = mLayer->crs();
235  layerMetadata = mLayer->htmlMetadata();
236 
237  mMapCanvas->setDestinationCrs( mLayer->crs() );
238  mMapCanvas->setLayers( QList< QgsMapLayer * >() << mLayer.get() );
239  mMapCanvas->zoomToFullExtent();
240 
241  if ( mAttributesTab && mLayer->type() != QgsMapLayer::VectorLayer )
242  {
243  mTabWidget->removeTab( mTabWidget->indexOf( mAttributesTab ) );
244  mAttributesTab = nullptr;
245  }
246  }
247 
248  QString myStyle = QgsApplication::reportStyleSheet();
249  mMetadataTextBrowser->document()->setDefaultStyleSheet( myStyle );
250  mMetadataTextBrowser->setHtml( layerMetadata );
251 
252 // report if layer was set to to project crs without prompt (may give a false positive)
253  if ( defaultProjectionOption == QLatin1String( "prompt" ) )
254  {
255  QgsCoordinateReferenceSystem defaultCrs =
257  if ( layerCrs == defaultCrs )
258  mNoticeLabel->setText( "NOTICE: Layer CRS set from project (" + defaultCrs.authid() + ')' );
259  }
260 
261  if ( mNoticeLabel->text().isEmpty() )
262  {
263  mNoticeLabel->hide();
264  }
265 }
266 
267 void QgsBrowserLayerProperties::setCondensedMode( bool )
268 {
269 
270 }
271 
272 void QgsBrowserLayerProperties::urlClicked( const QUrl &url )
273 {
274  QFileInfo file( url.toLocalFile() );
275  if ( file.exists() && !file.isDir() )
276  QgsGui::instance()->nativePlatformInterface()->openFileExplorerAndSelectFile( url.toLocalFile() );
277  else
278  QDesktopServices::openUrl( url );
279 }
280 
281 void QgsBrowserLayerProperties::loadAttributeTable()
282 {
283  if ( !mLayer || !mLayer->isValid() || mLayer->type() != QgsMapLayer::VectorLayer )
284  return;
285 
286  // Initialize the cache
287  QgsVectorLayerCache *layerCache = new QgsVectorLayerCache( qobject_cast< QgsVectorLayer * >( mLayer.get() ), 1000, this );
288  layerCache->setCacheGeometry( false );
289  QgsAttributeTableModel *tableModel = new QgsAttributeTableModel( layerCache, this );
290  mAttributeTableFilterModel = new QgsAttributeTableFilterModel( nullptr, tableModel, this );
291  tableModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setLimit( 100 ) );
292  layerCache->setParent( tableModel );
293  tableModel->setParent( mAttributeTableFilterModel );
294 
295  mAttributeTable->setModel( mAttributeTableFilterModel );
296  tableModel->loadLayer();
297  QFont font = mAttributeTable->font();
298  int fontSize = font.pointSize();
299 #ifdef Q_OS_WIN
300  fontSize = std::max( fontSize - 1, 8 ); // bit less on windows, due to poor rendering of small point sizes
301 #else
302  fontSize = std::max( fontSize - 2, 6 );
303 #endif
304  font.setPointSize( fontSize );
305  mAttributeTable->setFont( font );
306 
307  // we can safely do this expensive operation here (unlike in the main attribute table), because at most we have only 100 rows...
308  mAttributeTable->resizeColumnsToContents();
309  mAttributeTable->resizeRowsToContents();
310  mAttributeTable->verticalHeader()->setVisible( false ); // maximize valuable table space
311  mAttributeTable->setAlternatingRowColors( true );
312 }
313 
314 QgsBrowserDirectoryProperties::QgsBrowserDirectoryProperties( QWidget *parent )
315  : QgsBrowserPropertiesWidget( parent )
316 
317 {
318  setupUi( this );
319 
320  mPathLabel = new QgsBrowserPropertiesWrapLabel( QString(), mHeaderWidget );
321  mHeaderGridLayout->addItem( new QWidgetItem( mPathLabel ), 0, 1 );
322 }
323 
324 void QgsBrowserDirectoryProperties::setItem( QgsDataItem *item )
325 {
326  QgsDirectoryItem *directoryItem = qobject_cast<QgsDirectoryItem *>( item );
327  if ( !item )
328  return;
329 
330  mPathLabel->setText( QDir::toNativeSeparators( directoryItem->dirPath() ) );
331  mDirectoryWidget = new QgsDirectoryParamWidget( directoryItem->dirPath(), this );
332  mLayout->addWidget( mDirectoryWidget );
333 }
334 
335 QgsBrowserPropertiesDialog::QgsBrowserPropertiesDialog( const QString &settingsSection, QWidget *parent )
336  : QDialog( parent )
337  , mSettingsSection( settingsSection )
338 {
339  setupUi( this );
340  QgsSettings settings;
341  restoreGeometry( settings.value( mSettingsSection + "/propertiesDialog/geometry" ).toByteArray() );
342 }
343 
344 QgsBrowserPropertiesDialog::~QgsBrowserPropertiesDialog()
345 {
346  QgsSettings settings;
347  settings.setValue( mSettingsSection + "/propertiesDialog/geometry", saveGeometry() );
348 }
349 
350 void QgsBrowserPropertiesDialog::setItem( QgsDataItem *item )
351 {
352  if ( !item )
353  return;
354 
355  mPropertiesWidget = QgsBrowserPropertiesWidget::createWidget( item, this );
356  mLayout->addWidget( mPropertiesWidget );
357  setWindowTitle( item->type() == QgsDataItem::Layer ? tr( "Layer Properties" ) : tr( "Directory Properties" ) );
358 }
359 
360 
361 //
362 // QgsDockBrowserTreeView
363 //
364 
365 QgsDockBrowserTreeView::QgsDockBrowserTreeView( QWidget *parent ) : QgsBrowserTreeView( parent )
366 {
367  setDragDropMode( QTreeView::DragDrop ); // sets also acceptDrops + dragEnabled
368  setSelectionMode( QAbstractItemView::ExtendedSelection );
369  setContextMenuPolicy( Qt::CustomContextMenu );
370  setHeaderHidden( true );
371  setDropIndicatorShown( true );
372 
373 }
374 
375 void QgsDockBrowserTreeView::setAction( QDropEvent *e )
376 {
377  // if this mime data come from layer tree, the proposed action will be MoveAction
378  // but for browser we really need CopyAction
379  if ( e->mimeData()->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) &&
380  e->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
381  {
382  e->setDropAction( Qt::CopyAction );
383  }
384 }
385 
386 void QgsDockBrowserTreeView::dragEnterEvent( QDragEnterEvent *e )
387 {
388  setAction( e );
389 
390  // accept drag enter so that our widget will not get ignored
391  // and drag events will not get passed to QgisApp
392  e->accept();
393 }
394 
395 void QgsDockBrowserTreeView::dragMoveEvent( QDragMoveEvent *e )
396 {
397  // do not accept drops above/below items
398  /*if ( dropIndicatorPosition() != QAbstractItemView::OnItem )
399  {
400  QgsDebugMsg("drag not on item");
401  e->ignore();
402  return;
403  }*/
404 
405  setAction( e );
406  QTreeView::dragMoveEvent( e );
407  // reset action because QTreeView::dragMoveEvent() accepts proposed action
408  setAction( e );
409 
410  if ( !e->mimeData()->hasFormat( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ) ) )
411  {
412  e->ignore();
413  return;
414  }
415 }
416 
417 void QgsDockBrowserTreeView::dropEvent( QDropEvent *e )
418 {
419  setAction( e );
420  QTreeView::dropEvent( e );
421  // reset action because QTreeView::dropEvent() accepts proposed action
422  setAction( e );
423 }
424 
425 
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
QgsMapLayer::LayerType mapLayerType() const
Returns QgsMapLayer::LayerType.
static QgsNative * nativePlatformInterface()
Returns the global native interface, which offers abstraction to the host OS&#39;s underlying public inte...
Definition: qgsgui.cpp:54
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
Type type() const
Definition: qgsdataitem.h:265
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.
Added in 3.2.
Definition: qgsmaplayer.h:111
bool restoreGeometry(QWidget *widget, const QString &keyName)
Restore the wigget geometry from settings.
LayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:106
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.
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:430
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
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