diff --git a/images/images.qrc b/images/images.qrc index 7db93727c21b..e8b7e373989b 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -858,6 +858,7 @@ themes/default/mIconModelInput.svg themes/default/mIndicatorTemporal.svg themes/default/mIndicatorTimeFromProject.svg + themes/default/mIndicatorOffline.svg themes/default/temporal_navigation/back.svg themes/default/temporal_navigation/forward.svg themes/default/temporal_navigation/next.svg diff --git a/images/themes/default/mIndicatorOffline.svg b/images/themes/default/mIndicatorOffline.svg new file mode 100644 index 000000000000..b36064e5e843 --- /dev/null +++ b/images/themes/default/mIndicatorOffline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/python/core/auto_generated/qgsmaplayer.sip.in b/python/core/auto_generated/qgsmaplayer.sip.in index b89622e8ac4c..adcead6062de 100644 --- a/python/core/auto_generated/qgsmaplayer.sip.in +++ b/python/core/auto_generated/qgsmaplayer.sip.in @@ -1598,6 +1598,12 @@ Emitted when the validity of this layer changed. .. versionadded:: 3.16 %End + void customPropertyChanged( const QString &key ); +%Docstring +Emitted when a custom property of the layer has been changed or removed. + +.. versionadded:: 3.18 +%End protected: diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 76e52fd77ec3..35f21f1086d0 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -52,6 +52,7 @@ SET(QGIS_APP_SRCS qgslayertreeviewnonremovableindicator.cpp qgslayertreeviewbadlayerindicator.cpp qgslayertreeviewtemporalindicator.cpp + qgslayertreeviewofflineindicator.cpp qgsmapcanvasdockwidget.cpp qgsmapsavedialog.cpp qgsprojectlistitemdelegate.cpp diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 63456b2d5b7b..807b1acc7d8c 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -249,6 +249,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgslayertreeviewnonremovableindicator.h" #include "qgslayertreeviewnocrsindicator.h" #include "qgslayertreeviewtemporalindicator.h" +#include "qgslayertreeviewofflineindicator.h" #include "qgslayout.h" #include "qgslayoutatlas.h" #include "qgslayoutcustomdrophandler.h" @@ -4750,6 +4751,7 @@ void QgisApp::initLayerTreeView() new QgsLayerTreeViewMemoryIndicatorProvider( mLayerTreeView ); // gets parented to the layer view new QgsLayerTreeViewTemporalIndicatorProvider( mLayerTreeView ); // gets parented to the layer view new QgsLayerTreeViewNoCrsIndicatorProvider( mLayerTreeView ); // gets parented to the layer view + new QgsLayerTreeViewOfflineIndicatorProvider( mLayerTreeView ); // gets parented to the layer view QgsLayerTreeViewBadLayerIndicatorProvider *badLayerIndicatorProvider = new QgsLayerTreeViewBadLayerIndicatorProvider( mLayerTreeView ); // gets parented to the layer view connect( badLayerIndicatorProvider, &QgsLayerTreeViewBadLayerIndicatorProvider::requestChangeDataSource, this, &QgisApp::changeDataSource ); new QgsLayerTreeViewNonRemovableIndicatorProvider( mLayerTreeView ); // gets parented to the layer view diff --git a/src/app/qgslayertreeviewofflineindicator.cpp b/src/app/qgslayertreeviewofflineindicator.cpp new file mode 100644 index 000000000000..f92874d108a4 --- /dev/null +++ b/src/app/qgslayertreeviewofflineindicator.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + qgslayertreeviewofflineindicator.cpp + -------------------------------------- + Date : October 2020 + Copyright : (C) 2020 by David Signer + Email : david at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgslayertreeviewofflineindicator.h" +#include "qgslayertreeview.h" +#include "qgslayertree.h" +#include "qgslayertreemodel.h" +#include "qgisapp.h" + +QgsLayerTreeViewOfflineIndicatorProvider::QgsLayerTreeViewOfflineIndicatorProvider( QgsLayerTreeView *view ) + : QgsLayerTreeViewIndicatorProvider( view ) +{ + +} + +void QgsLayerTreeViewOfflineIndicatorProvider::connectSignals( QgsMapLayer *layer ) +{ + if ( !layer ) + return; + + connect( layer, &QgsMapLayer::customPropertyChanged, this, &QgsLayerTreeViewOfflineIndicatorProvider::onLayerChanged ); +} + +void QgsLayerTreeViewOfflineIndicatorProvider::disconnectSignals( QgsMapLayer *layer ) +{ + if ( !layer ) + return; + + disconnect( layer, &QgsMapLayer::customPropertyChanged, this, &QgsLayerTreeViewOfflineIndicatorProvider::onLayerChanged ); +} + +bool QgsLayerTreeViewOfflineIndicatorProvider::acceptLayer( QgsMapLayer *layer ) +{ + return layer->customProperty( QStringLiteral( "isOfflineEditable" ), false ).toBool(); +} + +QString QgsLayerTreeViewOfflineIndicatorProvider::iconName( QgsMapLayer *layer ) +{ + Q_UNUSED( layer ) + return QStringLiteral( "/mIndicatorOffline.svg" ); +} + +QString QgsLayerTreeViewOfflineIndicatorProvider::tooltipText( QgsMapLayer *layer ) +{ + Q_UNUSED( layer ) + return tr( "Offline layer" ); +} + +void QgsLayerTreeViewOfflineIndicatorProvider::onLayerChanged() +{ + QgsMapLayer *layer = qobject_cast( sender() ); + if ( !layer ) + return; + + updateLayerIndicator( layer ); +} diff --git a/src/app/qgslayertreeviewofflineindicator.h b/src/app/qgslayertreeviewofflineindicator.h new file mode 100644 index 000000000000..c6e9bdcc3ec9 --- /dev/null +++ b/src/app/qgslayertreeviewofflineindicator.h @@ -0,0 +1,47 @@ +/*************************************************************************** + qgslayertreeviewofflineindicator.h + -------------------------------------- + Date : October 2020 + Copyright : (C) 2020 by David Signer + Email : david at opengis dot ch + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSLAYERTREEVIEWOFFLINEINDICATOR_H +#define QGSLAYERTREEVIEWOFFLINEINDICATOR_H + +#include "qgslayertreeviewindicatorprovider.h" + +#include +#include + +class QgsLayerTreeNode; +class QgsLayerTreeView; + +//! Adds indicators showing whether layers are offline. +class QgsLayerTreeViewOfflineIndicatorProvider : public QgsLayerTreeViewIndicatorProvider +{ + Q_OBJECT + public: + explicit QgsLayerTreeViewOfflineIndicatorProvider( QgsLayerTreeView *view ); + + protected: + void connectSignals( QgsMapLayer *layer ) override; + void disconnectSignals( QgsMapLayer *layer ) override; + + protected slots: + void onLayerChanged(); + + private: + bool acceptLayer( QgsMapLayer *layer ) override; + QString iconName( QgsMapLayer *layer ) override; + QString tooltipText( QgsMapLayer *layer ) override; +}; + +#endif // QGSLAYERTREEVIEWOFFLINEINDICATOR_H diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index c6dddfdd026b..bd5d3b8fa826 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -1707,7 +1707,11 @@ QStringList QgsMapLayer::customPropertyKeys() const void QgsMapLayer::setCustomProperty( const QString &key, const QVariant &value ) { - mCustomProperties.setValue( key, value ); + if ( !mCustomProperties.contains( key ) || mCustomProperties.value( key ) != value ) + { + mCustomProperties.setValue( key, value ); + emit customPropertyChanged( key ); + } } void QgsMapLayer::setCustomProperties( const QgsObjectCustomProperties &properties ) @@ -1727,7 +1731,12 @@ QVariant QgsMapLayer::customProperty( const QString &value, const QVariant &defa void QgsMapLayer::removeCustomProperty( const QString &key ) { - mCustomProperties.remove( key ); + + if ( mCustomProperties.contains( key ) ) + { + mCustomProperties.remove( key ); + emit customPropertyChanged( key ); + } } QgsError QgsMapLayer::error() const diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index 40399273f2f6..02964f912c74 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -1429,6 +1429,12 @@ class CORE_EXPORT QgsMapLayer : public QObject */ void isValidChanged(); + /** + * Emitted when a custom property of the layer has been changed or removed. + * + * \since QGIS 3.18 + */ + void customPropertyChanged( const QString &key ); private slots: diff --git a/tests/src/python/test_qgsvectorlayer.py b/tests/src/python/test_qgsvectorlayer.py index 0f135c159420..a51d8c2ad2e9 100644 --- a/tests/src/python/test_qgsvectorlayer.py +++ b/tests/src/python/test_qgsvectorlayer.py @@ -399,6 +399,46 @@ def testSetDataSourceInvalidToValid(self): # should STILL have kept renderer! self.assertEqual(layer.renderer(), r) + def testSetCustomProperty(self): + """ + Test setting a custom property of the layer + """ + layer = createLayerWithOnePoint() + layer.setCustomProperty('Key_0', 'Value_0') + layer.setCustomProperty('Key_1', 'Value_1') + + spy = QSignalSpy(layer.customPropertyChanged) + + # change nothing by setting the same value + layer.setCustomProperty('Key_0', 'Value_0') + layer.setCustomProperty('Key_1', 'Value_1') + self.assertEqual(len(spy), 0) + + # change one + layer.setCustomProperty('Key_0', 'Value zero') + self.assertEqual(len(spy), 1) + + # add one + layer.setCustomProperty('Key_2', 'Value two') + self.assertEqual(len(spy), 2) + + # add a null one and an empty one + layer.setCustomProperty('Key_3', None) + layer.setCustomProperty('Key_4', '') + self.assertEqual(len(spy), 4) + + # remove one + layer.removeCustomProperty('Key_0') + self.assertEqual(len(spy), 5) + + self.assertEqual(layer.customProperty('Key_0', 'no value'), 'no value') + self.assertEqual(layer.customProperty('Key_1', 'no value'), 'Value_1') + self.assertEqual(layer.customProperty('Key_2', 'no value'), 'Value two') + self.assertEqual(layer.customProperty('Key_3', 'no value'), None) + self.assertEqual(layer.customProperty('Key_4', 'no value'), '') + + self.assertEqual(len(spy), 5) + def testStoreWkbTypeInvalidLayers(self): """ Test that layer wkb types are restored for projects with invalid layer paths