Skip to content

Commit

Permalink
[feature] Allow users to create notes for map layers
Browse files Browse the repository at this point in the history
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 ceff950
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 3 deletions.
1 change: 1 addition & 0 deletions images/images.qrc
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions images/themes/default/mIndicatorNotes.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/app/qgisapp.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions src/app/qgsapplayertreeviewmenuprovider.cpp
Expand Up @@ -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 )
Expand Down Expand Up @@ -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 );
}
Expand Down
97 changes: 97 additions & 0 deletions src/app/qgslayernotesmanager.cpp
@@ -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();
}
73 changes: 73 additions & 0 deletions src/app/qgslayernotesmanager.h
@@ -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
72 changes: 72 additions & 0 deletions src/app/qgslayertreeviewnotesindicator.cpp
@@ -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 );
}

41 changes: 41 additions & 0 deletions src/app/qgslayertreeviewnotesindicator.h
@@ -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
3 changes: 0 additions & 3 deletions src/gui/qgscolorbutton.cpp
Expand Up @@ -72,11 +72,8 @@ QgsColorButton::QgsColorButton( QWidget *parent, const QString &cdt, QgsColorSch
{
setButtonBackground();
} );

}



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

0 comments on commit ceff950

Please sign in to comment.