Skip to content
Permalink
Browse files
[feature] Allow users to create notes for map layers
These notes are saved per layer, per project, and can be used as
place to store important messages for users of the project like
to do lists, etc.

Notes can be created via the "Add Layer Notes" action in the
layer right click menu. Any layers with notes will show a little
notepad indicator icon to alert users as to the notes. Clicking
the indicator will edit the note.

Sponsored by Alta Ehf
  • Loading branch information
nyalldawson committed Apr 22, 2021
1 parent b0c09ac commit ceff9509606669a567c2836fe7b0b56cb7117e8f
@@ -914,6 +914,7 @@
<file>themes/default/mIconSnappingEndpoint.svg</file>
<file>themes/default/mActionStreamingDigitize.svg</file>
<file>themes/default/mActionEditHtml.svg</file>
<file>themes/default/mIndicatorNotes.svg</file>
</qresource>
<qresource prefix="/images/tips">
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
@@ -0,0 +1 @@
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M 2.3398438 2.5 C 1.8744838 2.5 1.5 2.8744838 1.5 3.3398438 L 1.5 13.660156 C 1.5 14.125516 1.8744838 14.5 2.3398438 14.5 L 10.40625 14.5 L 14.5 10.40625 L 14.5 3.3398438 C 14.5 2.8744838 14.125516 2.5 13.660156 2.5 L 2.3398438 2.5 z" fill="none" stroke="#2c2c2c" stroke-opacity=".70588237"/><path d="M 5.5,11.5 H 8" fill="#2e4e72" fill-rule="evenodd" stroke="#2c2c2c" stroke-width=".999998" stroke-linecap="square" stroke-linejoin="bevel" stroke-opacity=".706"/><path d="M5.5 5.5h6M5.5 8.5h6M12.5 3.5v-2M9.5 3.5v-2M6.5 3.5v-2M3.5 3.5v-2" fill="#2e4e72" fill-rule="evenodd" stroke="#2c2c2c" stroke-linecap="square" stroke-linejoin="bevel" stroke-opacity=".706"/><path d="m 13.560003,2.935202 -11.0570028,0.055 c -0.3260001,-0.035 -0.5630002,0.089 -0.5020001,0.5 v 10.015625 c -0.064,0.594 0.3963172,0.508116 0.5,0.5 H 3.0000003 V 3.990202 H 14.000001 v -0.5 c 0,-0.178 0.04698,-0.55 -0.499998,-0.5 z" fill="gray" fill-opacity="0.424"/><path d="M 9.9999998,10.499999 H 14" fill="#2e4e72" fill-rule="evenodd" stroke="#2c2c2c" stroke-width=".999997" stroke-linejoin="bevel" stroke-opacity=".706"/><path d="m 10.500002,11 v 3" fill="#2e4e72" fill-rule="evenodd" stroke="#2c2c2c" stroke-width=".999996" stroke-linejoin="bevel" stroke-opacity=".706"/></svg>
@@ -44,12 +44,14 @@ set(QGIS_APP_SRCS
qgsidentifyresultsdialog.cpp
qgsfeatureaction.cpp
qgslayercapabilitiesmodel.cpp
qgslayernotesmanager.cpp
qgslayertreeviewindicatorprovider.cpp
qgslayertreeviewembeddedindicator.cpp
qgslayertreeviewfilterindicator.cpp
qgslayertreeviewmemoryindicator.cpp
qgslayertreeviewnocrsindicator.cpp
qgslayertreeviewnonremovableindicator.cpp
qgslayertreeviewnotesindicator.cpp
qgslayertreeviewbadlayerindicator.cpp
qgslayertreeviewtemporalindicator.cpp
qgslayertreeviewofflineindicator.cpp
@@ -254,6 +254,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgslayertreeviewmemoryindicator.h"
#include "qgslayertreeviewbadlayerindicator.h"
#include "qgslayertreeviewnonremovableindicator.h"
#include "qgslayertreeviewnotesindicator.h"
#include "qgslayertreeviewnocrsindicator.h"
#include "qgslayertreeviewtemporalindicator.h"
#include "qgslayertreeviewofflineindicator.h"
@@ -4614,6 +4615,7 @@ void QgisApp::initLayerTreeView()
new QgsLayerTreeViewFilterIndicatorProvider( mLayerTreeView ); // gets parented to the layer view
new QgsLayerTreeViewEmbeddedIndicatorProvider( mLayerTreeView ); // gets parented to the layer view
new QgsLayerTreeViewMemoryIndicatorProvider( mLayerTreeView ); // gets parented to the layer view
new QgsLayerTreeViewNotesIndicatorProvider( 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
@@ -45,7 +45,9 @@
#include "qgsmessagebar.h"
#include "qgspointcloudlayer.h"
#include "qgsvectorlayerlabeling.h"
#include "qgslayernotesmanager.h"

#include <QMessageBox>

QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas *canvas )
: mView( view )
@@ -671,6 +673,26 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
}
}

QAction *notes = new QAction( QgsLayerNotesManager::layerHasNotes( layer ) ? tr( "Edit Layer Notes…" ) : tr( "Add Layer Notes…" ), menu );
connect( notes, &QAction::triggered, this, [layer ]
{
QgsLayerNotesManager::editLayerNotes( layer, QgisApp::instance() );
} );
menu->addAction( notes );
if ( QgsLayerNotesManager::layerHasNotes( layer ) )
{
QAction *notes = new QAction( tr( "Remove Layer Notes" ), menu );
connect( notes, &QAction::triggered, this, [layer ]
{
if ( QMessageBox::question( QgisApp::instance(),
tr( "Remove Layer Notes" ),
tr( "Are you sure you want to remove all notes for the layer “%1”?" ).arg( layer->name() ),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
QgsLayerNotesManager::removeNotes( layer );
} );
menu->addAction( notes );
}

if ( layer && QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
menu->addAction( tr( "&Properties…" ), QgisApp::instance(), &QgisApp::layerProperties );
}
@@ -0,0 +1,97 @@
/***************************************************************************
qgslayernotesmanager.cpp
--------------------------------------
Date : April 2021
Copyright : (C) 2021 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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 "qgslayernotesmanager.h"
#include "qgsmaplayer.h"
#include "qgsrichtexteditor.h"
#include "qgsgui.h"
#include <QDialogButtonBox>
#include <QPushButton>

QString QgsLayerNotesManager::layerNotes( QgsMapLayer *layer )
{
if ( !layer )
return nullptr;

return layer->customProperty( QStringLiteral( "userNotes" ) ).toString();
}

void QgsLayerNotesManager::setLayerNotes( QgsMapLayer *layer, const QString &notes )
{
if ( !layer )
return;

if ( notes.isEmpty() )
layer->removeCustomProperty( QStringLiteral( "userNotes" ) );
else
layer->setCustomProperty( QStringLiteral( "userNotes" ), notes );
}

bool QgsLayerNotesManager::layerHasNotes( QgsMapLayer *layer )
{
if ( !layer )
return false;

return !layer->customProperty( QStringLiteral( "userNotes" ) ).toString().isEmpty();
}

void QgsLayerNotesManager::removeNotes( QgsMapLayer *layer )
{
if ( layer )
layer->removeCustomProperty( QStringLiteral( "userNotes" ) );
}

void QgsLayerNotesManager::editLayerNotes( QgsMapLayer *layer, QWidget *parent )
{
const QString notes = layerNotes( layer );
QgsLayerNotesDialog *editor = new QgsLayerNotesDialog( parent );
editor->setNotes( notes );
editor->setWindowTitle( QObject::tr( "Layer Notes — %1" ).arg( layer->name() ) );
if ( editor->exec() )
{
QgsLayerNotesManager::setLayerNotes( layer, editor->notes() );
}
}

//
// QgsLayerNotesDialog
//
QgsLayerNotesDialog::QgsLayerNotesDialog( QWidget *parent )
: QDialog( parent, Qt::Tool )
{
QVBoxLayout *layout = new QVBoxLayout();
mEditor = new QgsRichTextEditor();
layout->addWidget( mEditor );

QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Save | QDialogButtonBox::Cancel );
connect( buttonBox->button( QDialogButtonBox::Save ), &QPushButton::clicked, this, &QDialog::accept );
connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
layout->addWidget( buttonBox );

layout->setContentsMargins( 3, 0, 3, 3 );
setLayout( layout );

QgsGui::enableAutoGeometryRestore( this );
}

void QgsLayerNotesDialog::setNotes( const QString &notes )
{
mEditor->setText( notes );
}

QString QgsLayerNotesDialog::notes() const
{
return mEditor->toHtml();
}
@@ -0,0 +1,73 @@
/***************************************************************************
qgslayernotesmanager.h
--------------------------------------
Date : April 2021
Copyright : (C) 2021 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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 QGSLAYERNOTESMANAGER_H
#define QGSLAYERNOTESMANAGER_H

#include <QString>
#include <QDialog>

class QgsMapLayer;
class QWidget;
class QgsRichTextEditor;

class QgsLayerNotesManager
{
public:

/**
* Returns the notes for the specified \a layer.
*
* The returned string is a HTML formatted set of user notations for the layer.
*/
static QString layerNotes( QgsMapLayer *layer );

/**
* Sets the \a notes for the specified \a layer, where \a notes is a HTML formatted string.
*/
static void setLayerNotes( QgsMapLayer *layer, const QString &notes );

/**
* Returns TRUE if the specified \a layer has notes available.
*/
static bool layerHasNotes( QgsMapLayer *layer );

/**
* Removes any notes for the specified \a layer.
*/
static void removeNotes( QgsMapLayer *layer );

/**
* Shows a dialog allowing users to edit the notes for the specified \a layer.
*/
static void editLayerNotes( QgsMapLayer *layer, QWidget *parent );
};

class QgsLayerNotesDialog : public QDialog
{
Q_OBJECT

public:

QgsLayerNotesDialog( QWidget *parent );

void setNotes( const QString &notes );
QString notes() const;

private:
QgsRichTextEditor *mEditor = nullptr;
};

#endif // QGSLAYERNOTESMANAGER_H
@@ -0,0 +1,72 @@
/***************************************************************************
qgslayertreeviewnotesindicator.h
--------------------------------------
Date : April 2021
Copyright : (C) 2021 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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 "qgslayertreeviewnotesindicator.h"
#include "qgslayertreeview.h"
#include "qgslayertree.h"
#include "qgslayertreemodel.h"
#include "qgslayertreeutils.h"
#include "qgsvectorlayer.h"
#include "qgslayernotesmanager.h"
#include "qgisapp.h"

QgsLayerTreeViewNotesIndicatorProvider::QgsLayerTreeViewNotesIndicatorProvider( QgsLayerTreeView *view )
: QgsLayerTreeViewIndicatorProvider( view )
{
}

void QgsLayerTreeViewNotesIndicatorProvider::onIndicatorClicked( const QModelIndex &index )
{
QgsLayerTreeNode *node = mLayerTreeView->index2node( index );
if ( !QgsLayerTree::isLayer( node ) )
return;

if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
{
QgsLayerNotesManager::editLayerNotes( layer, QgisApp::instance() );
}
}

void QgsLayerTreeViewNotesIndicatorProvider::connectSignals( QgsMapLayer *layer )
{
QgsLayerTreeViewIndicatorProvider::connectSignals( layer );
connect( layer, &QgsMapLayer::customPropertyChanged, this, &QgsLayerTreeViewNotesIndicatorProvider::onLayerChanged );
}

void QgsLayerTreeViewNotesIndicatorProvider::disconnectSignals( QgsMapLayer *layer )
{
QgsLayerTreeViewIndicatorProvider::disconnectSignals( layer );
disconnect( layer, &QgsMapLayer::customPropertyChanged, this, &QgsLayerTreeViewNotesIndicatorProvider::onLayerChanged );
}

bool QgsLayerTreeViewNotesIndicatorProvider::acceptLayer( QgsMapLayer *layer )
{
if ( !layer )
return false;

return QgsLayerNotesManager::layerHasNotes( layer );
}

QString QgsLayerTreeViewNotesIndicatorProvider::iconName( QgsMapLayer *layer )
{
Q_UNUSED( layer )
return QStringLiteral( "/mIndicatorNotes.svg" );
}

QString QgsLayerTreeViewNotesIndicatorProvider::tooltipText( QgsMapLayer *layer )
{
return QgsLayerNotesManager::layerNotes( layer );
}

@@ -0,0 +1,41 @@
/***************************************************************************
qgslayertreeviewnotesindicator.h
--------------------------------------
Date : April 2021
Copyright : (C) 2021 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* 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 QGSLAYERTREEVIEWNOTESINDICATOR_H
#define QGSLAYERTREEVIEWNOTESINDICATOR_H

#include "qgslayertreeviewindicatorprovider.h"

//! Adds indicators showing whether layers have notes attached.
class QgsLayerTreeViewNotesIndicatorProvider : public QgsLayerTreeViewIndicatorProvider
{
Q_OBJECT
public:
explicit QgsLayerTreeViewNotesIndicatorProvider( QgsLayerTreeView *view );

protected slots:

void onIndicatorClicked( const QModelIndex &index ) override;
protected:
void connectSignals( QgsMapLayer *layer ) override ;
void disconnectSignals( QgsMapLayer *layer ) override;

private:
bool acceptLayer( QgsMapLayer *layer ) override;
QString iconName( QgsMapLayer *layer ) override;
QString tooltipText( QgsMapLayer *layer ) override;
};

#endif // QGSLAYERTREEVIEWNOTESINDICATOR_H
@@ -72,11 +72,8 @@ QgsColorButton::QgsColorButton( QWidget *parent, const QString &cdt, QgsColorSch
{
setButtonBackground();
} );

}



QSize QgsColorButton::minimumSizeHint() const
{
return mMinimumSize;

0 comments on commit ceff950

Please sign in to comment.