Annotations are now handled at a project level

This commit implements a new QgsAnnotationManager class, which
handles storage, writing and retrieval of annotations.

QgsProject has an annotationManager() attached to it. Map canvases
sync their visible QgsMapCanvasAnnotationItems to the annotations
contained within the project's annotation manager.

This moves all management, storage and retrieval of annotations
up to core and out of app/canvas.
This commit is contained in:
Nyall Dawson 2017-01-30 14:57:16 +10:00
parent dd51843e54
commit 4058f4734b
33 changed files with 398 additions and 184 deletions

View File

@ -1,8 +1,26 @@
%ModuleHeaderCode
#include <qgshtmlannotation.h>
#include <qgssvgannotation.h>
#include <qgstextannotation.h>
%End
class QgsAnnotation : QObject
{
%TypeHeaderCode
#include <qgsannotation.h>
%End
%ConvertToSubClassCode
if ( dynamic_cast< QgsTextAnnotation* > ( sipCpp ) )
sipType = sipType_QgsTextAnnotation;
else if ( dynamic_cast< QgsSvgAnnotation* > ( sipCpp ) )
sipType = sipType_QgsSvgAnnotation;
else if ( dynamic_cast< QgsHtmlAnnotation* > ( sipCpp ) )
sipType = sipType_QgsHtmlAnnotation;
else
sipType = NULL;
%End
public:
QgsAnnotation( QObject* parent /TransferThis/ = nullptr );

View File

@ -0,0 +1,24 @@
class QgsAnnotationManager : QObject
{
%TypeHeaderCode
#include <qgsannotationmanager.h>
%End
public:
explicit QgsAnnotationManager( QgsProject* project = nullptr );
~QgsAnnotationManager();
bool addAnnotation( QgsAnnotation* annotation /Transfer/ );
bool removeAnnotation( QgsAnnotation* annotation );
void clear();
QList< QgsAnnotation* > annotations() const;
bool readXml( const QDomElement& element, const QDomDocument& doc );
QDomElement writeXml( QDomDocument& doc ) const;
signals:
void annotationAdded( QgsAnnotation* annotation );
void annotationRemoved();
void annotationAboutToBeRemoved( QgsAnnotation* annotation );
};

View File

@ -19,6 +19,8 @@ class QgsHtmlAnnotation : QgsAnnotation
virtual void setAssociatedFeature( const QgsFeature& feature );
static QgsHtmlAnnotation* create() /Factory/;
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;

View File

@ -12,7 +12,9 @@ class QgsSvgAnnotation : QgsAnnotation
void setFilePath( const QString& file );
QString filePath() const;
static QgsSvgAnnotation* create() /Factory/;
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;

View File

@ -13,6 +13,8 @@ class QgsTextAnnotation : QgsAnnotation
virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc );
static QgsTextAnnotation* create() /Factory/;
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const;

View File

@ -258,8 +258,8 @@ class QgsComposerMap : QgsComposerItem
/** Sets canvas pointer (necessary to query and draw map canvas items)*/
void setMapCanvas( QGraphicsView* canvas /Transfer/ );
void setDrawCanvasItems( bool b );
bool drawCanvasItems() const;
void setDrawAnnotations( bool draw );
bool drawAnnotations() const;
/** Returns the conversion factor map units -> mm*/
double mapUnitsToMM() const;

View File

@ -175,6 +175,7 @@
%Include qgsxmlutils.sip
%Include annotations/qgsannotation.sip
%Include annotations/qgsannotationmanager.sip
%Include annotations/qgshtmlannotation.sip
%Include annotations/qgssvgannotation.sip
%Include annotations/qgstextannotation.sip

View File

@ -325,6 +325,8 @@ class QgsProject : QObject, QgsExpressionContextGenerator
*/
QgsMapThemeCollection* mapThemeCollection();
QgsAnnotationManager* annotationManager();
/**
* Set a list of layers which should not be taken into account on map identification
*/

View File

@ -683,7 +683,7 @@ void QgsComposerMapWidget::updateGuiElements()
mKeepLayerStylesCheckBox->setCheckState( mComposerMap->keepLayerStyles() ? Qt::Checked : Qt::Unchecked );
//draw canvas items
if ( mComposerMap->drawCanvasItems() )
if ( mComposerMap->drawAnnotations() )
{
mDrawCanvasItemsCheckBox->setCheckState( Qt::Checked );
}
@ -951,7 +951,7 @@ void QgsComposerMapWidget::on_mDrawCanvasItemsCheckBox_stateChanged( int state )
}
mComposerMap->beginCommand( tr( "Canvas items toggled" ) );
mComposerMap->setDrawCanvasItems( state == Qt::Checked );
mComposerMap->setDrawAnnotations( state == Qt::Checked );
mUpdatePreviewButton->setEnabled( false ); //prevent crashes because of many button clicks
mComposerMap->setCacheUpdated( false );
mComposerMap->cache();

View File

@ -106,6 +106,8 @@
#include "qgsapplayertreeviewmenuprovider.h"
#include "qgsapplication.h"
#include "qgsactionmanager.h"
#include "qgsannotationmanager.h"
#include "qgsannotationregistry.h"
#include "qgsattributetabledialog.h"
#include "qgsattributedialog.h"
#include "qgsauthmanager.h"
@ -777,6 +779,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
functionProfile( &QgisApp::updateRecentProjectPaths, this, QStringLiteral( "Update recent project paths" ) );
functionProfile( &QgisApp::updateProjectFromTemplates, this, QStringLiteral( "Update project from templates" ) );
functionProfile( &QgisApp::legendLayerSelectionChanged, this, QStringLiteral( "Legend layer selection changed" ) );
QgsApplication::annotationRegistry()->addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "FormAnnotationItem" ), &QgsFormAnnotation::create ) );
connect( QgsProject::instance()->annotationManager(), &QgsAnnotationManager::annotationAdded, this, &QgisApp::annotationCreated );
mSaveRollbackInProgress = false;
QFileSystemWatcher* projectsTemplateWatcher = new QFileSystemWatcher( this );
@ -1416,6 +1422,12 @@ void QgisApp::dropEventTimeout()
mMapCanvas->refresh();
}
void QgisApp::annotationCreated( QgsAnnotation* annotation )
{
// create canvas annotation item for annotation
QgsMapCanvasAnnotationItem* canvasItem = new QgsMapCanvasAnnotationItem( annotation, mMapCanvas );
Q_UNUSED( canvasItem ); //item is already added automatically to canvas scene
}
void QgisApp::registerCustomDropHandler( QgsCustomDropHandler* handler )
{
@ -1817,31 +1829,6 @@ void QgisApp::showStyleManager()
dlg.exec();
}
void QgisApp::writeAnnotationItemsToProject( QDomDocument& doc )
{
QDomElement documentElem = doc.documentElement();
if ( documentElem.isNull() )
{
return;
}
QList<QgsMapCanvasAnnotationItem*> items = annotationItems();
QgsMapCanvasAnnotationItem* item = nullptr;
QListIterator<QgsMapCanvasAnnotationItem*> i( items );
// save lowermost annotation (at end of list) first
i.toBack();
while ( i.hasPrevious() )
{
item = i.previous();
if ( !item || !item->annotation() )
{
continue;
}
item->annotation()->writeXml( documentElem, doc );
}
}
void QgisApp::showPythonDialog()
{
if ( !mPythonUtils || !mPythonUtils->isEnabled() )
@ -2831,11 +2818,8 @@ void QgisApp::setupConnections()
this, SLOT( readProject( const QDomDocument & ) ) );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ),
this, SLOT( writeProject( QDomDocument & ) ) );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument& ) ),
this, SLOT( writeAnnotationItemsToProject( QDomDocument& ) ) );
connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( loadComposersFromProject( const QDomDocument& ) ) );
connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( loadAnnotationItemsFromProject( const QDomDocument& ) ) );
connect( this, SIGNAL( projectRead() ),
this, SLOT( fileOpenedOKAfterLaunch() ) );
@ -6936,70 +6920,6 @@ void QgisApp::on_mPrintComposersMenu_aboutToShow()
mPrintComposersMenu->addActions( acts );
}
bool QgisApp::loadAnnotationItemsFromProject( const QDomDocument& doc )
{
if ( !mMapCanvas )
{
return false;
}
removeAnnotationItems();
if ( doc.isNull() )
{
return false;
}
QList< QgsAnnotation* > annotations;
QDomNodeList textItemList = doc.elementsByTagName( QStringLiteral( "TextAnnotationItem" ) );
for ( int i = 0; i < textItemList.size(); ++i )
{
QgsTextAnnotation* newTextItem = new QgsTextAnnotation();
newTextItem->readXml( textItemList.at( i ).toElement(), doc );
annotations << newTextItem;
}
QDomNodeList formItemList = doc.elementsByTagName( QStringLiteral( "FormAnnotationItem" ) );
for ( int i = 0; i < formItemList.size(); ++i )
{
QgsFormAnnotation* newFormItem = new QgsFormAnnotation();
newFormItem->readXml( formItemList.at( i ).toElement(), doc );
annotations << newFormItem;
}
#ifdef WITH_QTWEBKIT
QDomNodeList htmlItemList = doc.elementsByTagName( QStringLiteral( "HtmlAnnotationItem" ) );
for ( int i = 0; i < htmlItemList.size(); ++i )
{
QgsHtmlAnnotation* newHtmlItem = new QgsHtmlAnnotation();
newHtmlItem->readXml( htmlItemList.at( i ).toElement(), doc );
annotations << newHtmlItem;
}
#endif
QDomNodeList svgItemList = doc.elementsByTagName( QStringLiteral( "SVGAnnotationItem" ) );
for ( int i = 0; i < svgItemList.size(); ++i )
{
QgsSvgAnnotation* newSvgItem = new QgsSvgAnnotation();
newSvgItem->readXml( svgItemList.at( i ).toElement(), doc );
annotations << newSvgItem;
}
Q_FOREACH ( QgsAnnotation* annotation, annotations )
{
if ( !annotation->mapPositionCrs().isValid() )
{
annotation->setMapPositionCrs( mMapCanvas->mapSettings().destinationCrs() );
}
// create canvas annotation items
QgsMapCanvasAnnotationItem* canvasItem = new QgsMapCanvasAnnotationItem( annotation, mMapCanvas );
Q_UNUSED( canvasItem ); //item is already added automatically to canvas scene
}
return true;
}
void QgisApp::showPinnedLabels( bool show )
{
qobject_cast<QgsMapToolPinLabels*>( mMapTools.mPinLabels )->showPinnedLabels( show );

View File

@ -39,6 +39,7 @@ class QValidator;
class QgisAppInterface;
class QgisAppStyleSheet;
class QgsAnnotation;
class QgsMapCanvasAnnotationItem;
class QgsAuthManager;
class QgsBookmarks;
@ -1308,16 +1309,12 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
void showStyleManager();
void writeAnnotationItemsToProject( QDomDocument& doc );
//! Creates the composer instances in a project file and adds them to the menu
bool loadComposersFromProject( const QDomDocument& doc );
//! Slot to handle display of composers menu, e.g. sorting
void on_mPrintComposersMenu_aboutToShow();
bool loadAnnotationItemsFromProject( const QDomDocument& doc );
//! Toggles whether to show pinned labels
void showPinnedLabels( bool show );
//! Activates pin labels tool
@ -1390,6 +1387,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
//! Handles processing of dropped mimedata
void dropEventTimeout();
void annotationCreated( QgsAnnotation* annotation );
signals:
/** Emitted when a key is pressed and we want non widget sublasses to be able

View File

@ -17,6 +17,8 @@
#include "qgsformannotation.h"
#include "qgsmapcanvasannotationitem.h"
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsannotationmanager.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QGraphicsScene>
@ -84,12 +86,8 @@ void QgsFormAnnotationDialog::on_mBrowseToolButton_clicked()
void QgsFormAnnotationDialog::deleteItem()
{
QGraphicsScene* scene = mItem->scene();
if ( scene )
{
scene->removeItem( mItem );
}
delete mItem;
if ( mItem && mItem->annotation() )
QgsProject::instance()->annotationManager()->removeAnnotation( mItem->annotation() );
mItem = nullptr;
}

View File

@ -17,6 +17,8 @@
#include "qgsannotationwidget.h"
#include "qgsmapcanvasannotationitem.h"
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsannotationmanager.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QGraphicsScene>
@ -83,12 +85,8 @@ void QgsHtmlAnnotationDialog::on_mBrowseToolButton_clicked()
void QgsHtmlAnnotationDialog::deleteItem()
{
QGraphicsScene* scene = mItem->scene();
if ( scene )
{
scene->removeItem( mItem );
}
delete mItem;
if ( mItem && mItem->annotation() )
QgsProject::instance()->annotationManager()->removeAnnotation( mItem->annotation() );
mItem = nullptr;
}

View File

@ -29,6 +29,7 @@
#include "qgssvgannotation.h"
#include "qgsproject.h"
#include "qgscsexception.h"
#include "qgsannotationmanager.h"
#include <QDialog>
#include <QMouseEvent>
@ -146,15 +147,11 @@ void QgsMapToolAnnotation::keyPressEvent( QKeyEvent* e )
{
if ( e->key() == Qt::Key_Backspace || e->key() == Qt::Key_Delete )
{
if ( mCanvas && mCanvas->scene() )
QCursor neutralCursor( item->cursorShapeForAction( QgsMapCanvasAnnotationItem::NoAction ) );
QgsProject::instance()->annotationManager()->removeAnnotation( item->annotation() );
if ( mCanvas )
{
QCursor neutralCursor( item->cursorShapeForAction( QgsMapCanvasAnnotationItem::NoAction ) );
mCanvas->scene()->removeItem( item );
delete item;
mCanvas->setCursor( neutralCursor );
QgsProject::instance()->setDirty( true ); // TODO QGIS3: Rework the whole annotation code to be MVC compliant, see PR #2506
// Override default shortcut management in MapCanvas
e->ignore();
}
}

View File

@ -19,6 +19,8 @@
#include "qgsannotationwidget.h"
#include "qgssvgannotation.h"
#include "qgsmapcanvasannotationitem.h"
#include "qgsproject.h"
#include "qgsannotationmanager.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QGraphicsScene>
@ -89,12 +91,8 @@ void QgsSvgAnnotationDialog::applySettingsToItem()
void QgsSvgAnnotationDialog::deleteItem()
{
QGraphicsScene* scene = mItem->scene();
if ( scene )
{
scene->removeItem( mItem );
}
delete mItem;
if ( mItem && mItem->annotation() )
QgsProject::instance()->annotationManager()->removeAnnotation( mItem->annotation() );
mItem = nullptr;
}

View File

@ -19,6 +19,8 @@
#include "qgsannotationwidget.h"
#include "qgstextannotation.h"
#include "qgsmapcanvasannotationitem.h"
#include "qgsannotationmanager.h"
#include "qgsproject.h"
#include <QColorDialog>
#include <QGraphicsScene>
@ -156,12 +158,8 @@ void QgsTextAnnotationDialog::blockAllSignals( bool block )
void QgsTextAnnotationDialog::deleteItem()
{
QGraphicsScene* scene = mItem->scene();
if ( scene )
{
scene->removeItem( mItem );
}
delete mItem;
if ( mItem && mItem->annotation() )
QgsProject::instance()->annotationManager()->removeAnnotation( mItem->annotation() );
mItem = nullptr;
}

View File

@ -780,6 +780,8 @@ SET(QGIS_CORE_HDRS
qgswebview.h
qgslocalec.h
annotations/qgsannotationregistry.h
auth/qgsauthcertutils.h
auth/qgsauthconfig.h
auth/qgsauthcrypto.h

View File

@ -353,7 +353,10 @@ void QgsAnnotation::_readXml( const QDomElement& annotationElem, const QDomDocum
QPointF pos;
pos.setX( annotationElem.attribute( QStringLiteral( "canvasPosX" ), QStringLiteral( "0" ) ).toDouble() );
pos.setY( annotationElem.attribute( QStringLiteral( "canvasPosY" ), QStringLiteral( "0" ) ).toDouble() );
mRelativePosition = pos;
if ( pos.x() >= 1 || pos.x() < 0 || pos.y() < 0 || pos.y() >= 1 )
mRelativePosition = QPointF();
else
mRelativePosition = pos;
QgsPoint mapPos;
mapPos.setX( annotationElem.attribute( QStringLiteral( "mapPosX" ), QStringLiteral( "0" ) ).toDouble() );
mapPos.setY( annotationElem.attribute( QStringLiteral( "mapPosY" ), QStringLiteral( "0" ) ).toDouble() );

View File

@ -16,6 +16,7 @@
#include "qgsannotationmanager.h"
#include "qgsproject.h"
#include "qgsannotation.h"
#include "qgsannotationregistry.h"
QgsAnnotationManager::QgsAnnotationManager( QgsProject* project )
: QObject( project )
@ -39,6 +40,7 @@ bool QgsAnnotationManager::addAnnotation( QgsAnnotation* annotation )
mAnnotations << annotation;
emit annotationAdded( annotation );
mProject->setDirty( true );
return true;
}
@ -54,6 +56,7 @@ bool QgsAnnotationManager::removeAnnotation( QgsAnnotation* annotation )
mAnnotations.removeAll( annotation );
delete annotation;
emit annotationRemoved();
mProject->setDirty( true );
return true;
}
@ -75,30 +78,73 @@ bool QgsAnnotationManager::readXml( const QDomElement& element, const QDomDocume
clear();
//restore each annotation
bool result = true;
QDomNodeList annotationNodes = element.elementsByTagName( QStringLiteral( "Annotations" ) );
QDomElement annotationsElem = element.firstChildElement( QStringLiteral( "Annotations" ) );
QDomNodeList annotationNodes = annotationsElem.elementsByTagName( QStringLiteral( "Annotation" ) );
for ( int i = 0; i < annotationNodes.size(); ++i )
{
QgsAnnotation* a = createAnnotationFromXml( annotationNodes.at( i ).toElement(), doc );
if ( !a )
{
result = false;
continue;
}
addAnnotation( a );
createAnnotationFromXml( annotationNodes.at( i ).toElement(), doc );
}
// restore old (pre 3.0) project annotations
QDomNodeList oldItemList = element.elementsByTagName( QStringLiteral( "TextAnnotationItem" ) );
for ( int i = 0; i < oldItemList.size(); ++i )
{
createAnnotationFromXml( oldItemList.at( i ).toElement(), doc );
}
oldItemList = element.elementsByTagName( QStringLiteral( "FormAnnotationItem" ) );
for ( int i = 0; i < oldItemList.size(); ++i )
{
createAnnotationFromXml( oldItemList.at( i ).toElement(), doc );
}
oldItemList = element.elementsByTagName( QStringLiteral( "HtmlAnnotationItem" ) );
for ( int i = 0; i < oldItemList.size(); ++i )
{
createAnnotationFromXml( oldItemList.at( i ).toElement(), doc );
}
oldItemList = element.elementsByTagName( QStringLiteral( "SVGAnnotationItem" ) );
for ( int i = 0; i < oldItemList.size(); ++i )
{
createAnnotationFromXml( oldItemList.at( i ).toElement(), doc );
}
return result;
}
QgsAnnotation* QgsAnnotationManager::createAnnotationFromXml( const QDomElement& element, const QDomDocument& ) const
QDomElement QgsAnnotationManager::writeXml( QDomDocument& doc ) const
{
QDomNodeList annotationNodeList = element.elementsByTagName( QStringLiteral( "Annotation" ) );
QDomElement annotationsElem = doc.createElement( QStringLiteral( "Annotations" ) );
QListIterator<QgsAnnotation*> i( mAnnotations );
// save lowermost annotation (at end of list) first
i.toBack();
while ( i.hasPrevious() )
{
QgsAnnotation* annotation = i.previous();
if ( annotationNodeList.size() > 0 )
{
return nullptr;
}
else
{
return nullptr;
if ( !annotation )
{
continue;
}
annotation->writeXml( annotationsElem, doc );
}
return annotationsElem;
}
void QgsAnnotationManager::createAnnotationFromXml( const QDomElement& element, const QDomDocument& doc )
{
QString type = element.tagName();
QgsAnnotation* annotation = QgsApplication::annotationRegistry()->create( type );
if ( !annotation )
return;
annotation->readXml( element, doc );
if ( !annotation->mapPositionCrs().isValid() )
{
annotation->setMapPositionCrs( mProject->crs() );
}
addAnnotation( annotation );
}

View File

@ -67,8 +67,19 @@ class CORE_EXPORT QgsAnnotationManager : public QObject
*/
QList< QgsAnnotation* > annotations() const;
/**
* Reads the manager's state from a DOM element, restoring all annotations
* present in the XML document.
* @see writeXml()
*/
bool readXml( const QDomElement& element, const QDomDocument& doc );
/**
* Returns a DOM element representing the state of the manager.
* @see readXml()
*/
QDomElement writeXml( QDomDocument& doc ) const;
signals:
//! Emitted when a annotation has been added to the manager
@ -86,7 +97,7 @@ class CORE_EXPORT QgsAnnotationManager : public QObject
QList< QgsAnnotation* > mAnnotations;
QgsAnnotation* createAnnotationFromXml( const QDomElement& element, const QDomDocument& doc ) const;
void createAnnotationFromXml( const QDomElement& element, const QDomDocument& doc );
};

View File

@ -0,0 +1,131 @@
/***************************************************************************
qgsannotationregistry.h
-----------------------
Date : January 2017
Copyright : (C) 2017 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 QGSANNOTATIONREGISTRY_H
#define QGSANNOTATIONREGISTRY_H
#include "qgis_core.h"
#include "qgsannotation.h"
#include "qgstextannotation.h"
#include "qgssvgannotation.h"
#include "qgshtmlannotation.h"
#include <QString>
#include <functional>
///@cond PRIVATE
// None of this is stable API!
//! Creates a new annotation object
typedef std::function < QgsAnnotation*() > QgsCreateAnnotationFunc;
/**
* \class QgsAnnotationMetadata
* \ingroup core
* Metadata item for an annotation type within a QgsAnnotationRegistry.
* \note Added in QGIS 3.0
*/
class CORE_EXPORT QgsAnnotationMetadata
{
public:
/**
* Constructor for QgsAnnotationMetadata. \a typeName should be a unique string
* identifying the annotation type.
*/
QgsAnnotationMetadata( const QString& typeName, QgsCreateAnnotationFunc createFunc )
: mTypeName( typeName )
, mCreateFunc( createFunc )
{}
/**
* Returns the annotation type.
*/
QString type() const { return mTypeName; }
/**
* Creates a new annotation of the associated type.
*/
QgsAnnotation* createAnnotation() const { return mCreateFunc ? mCreateFunc() : nullptr ; }
private:
QString mTypeName;
QgsCreateAnnotationFunc mCreateFunc = nullptr;
QgsAnnotationMetadata() = default;
friend class QMap< QString, QgsAnnotationMetadata >;
};
/**
* \class QgsAnnotationRegistry
* \ingroup core
* Handles registration and creation of annotation item types.
* \note Added in QGIS 3.0
*/
class CORE_EXPORT QgsAnnotationRegistry
{
public:
/**
* Constructor for QgsAnnotationRegistry. The registry is automatically populated
* with several standard annotation types.
*/
QgsAnnotationRegistry()
{
addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "TextAnnotationItem" ), QgsTextAnnotation::create ) );
#ifdef WITH_QTWEBKIT
addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "HtmlAnnotationItem" ), QgsHtmlAnnotation::create ) );
#endif
addAnnotationType( QgsAnnotationMetadata( QStringLiteral( "SVGAnnotationItem" ), QgsSvgAnnotation::create ) );
}
/**
* Adds a new annotation type to the registry. Returns true if adding the type
* was successful, or false if an annotation with duplicate type already exists
* in the registry.
*/
bool addAnnotationType( const QgsAnnotationMetadata& metadata )
{
if ( mMetadata.contains( metadata.type() ) )
return false;
mMetadata.insert( metadata.type(), metadata );
return true;
}
/**
* Creates a new annotation of the specified type. Returns nullptr if no
* matching annotations types were found.
*/
QgsAnnotation* create( const QString& typeName ) const
{
if ( !mMetadata.contains( typeName ) )
return nullptr;
return mMetadata.value( typeName ).createAnnotation();
}
private:
QMap< QString, QgsAnnotationMetadata > mMetadata;
};
///@endcond
#endif // QGSANNOTATIONREGISTRY_H

View File

@ -63,6 +63,11 @@ class CORE_EXPORT QgsHtmlAnnotation: public QgsAnnotation
void setAssociatedFeature( const QgsFeature& feature ) override;
/**
* Returns a new QgsHtmlAnnotation object.
*/
static QgsHtmlAnnotation* create() { return new QgsHtmlAnnotation(); }
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const override;

View File

@ -54,6 +54,11 @@ class CORE_EXPORT QgsSvgAnnotation: public QgsAnnotation
*/
QString filePath() const { return mFilePath; }
/**
* Returns a new QgsSvgAnnotation object.
*/
static QgsSvgAnnotation* create() { return new QgsSvgAnnotation(); }
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const override;

View File

@ -56,6 +56,11 @@ class CORE_EXPORT QgsTextAnnotation: public QgsAnnotation
virtual void writeXml( QDomElement& elem, QDomDocument & doc ) const override;
virtual void readXml( const QDomElement& itemElem, const QDomDocument& doc ) override;
/**
* Returns a new QgsTextAnnotation object.
*/
static QgsTextAnnotation* create() { return new QgsTextAnnotation(); }
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const override;

View File

@ -36,6 +36,7 @@
#include "qgsexpression.h"
#include "qgsmapthemecollection.h"
#include "qgsannotation.h"
#include "qgsannotationmanager.h"
#include "qgssymbollayerutils.h" //for pointOnLineWithDistance
@ -56,7 +57,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
, mFollowVisibilityPreset( false )
, mUpdatesEnabled( true )
, mMapCanvas( nullptr )
, mDrawCanvasItems( true )
, mDrawAnnotations( true )
, mAtlasDriven( false )
, mAtlasScalingMode( Auto )
, mAtlasMargin( 0.10 )
@ -101,7 +102,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
, mFollowVisibilityPreset( false )
, mUpdatesEnabled( true )
, mMapCanvas( nullptr )
, mDrawCanvasItems( true )
, mDrawAnnotations( true )
, mAtlasDriven( false )
, mAtlasScalingMode( Auto )
, mAtlasMargin( 0.10 )
@ -360,7 +361,7 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem*,
painter->restore();
//draw canvas items
drawCanvasItems( painter );
drawAnnotations( painter );
}
else if ( mComposition->plotStyle() == QgsComposition::Print ||
mComposition->plotStyle() == QgsComposition::Postscript )
@ -399,7 +400,7 @@ void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem*,
painter->restore();
//draw canvas items
drawCanvasItems( painter );
drawAnnotations( painter );
mDrawing = false;
}
@ -1202,7 +1203,7 @@ bool QgsComposerMap::writeXml( QDomElement& elem, QDomDocument & doc ) const
composerMapElem.setAttribute( QStringLiteral( "keepLayerSet" ), QStringLiteral( "false" ) );
}
if ( mDrawCanvasItems )
if ( mDrawAnnotations )
{
composerMapElem.setAttribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
}
@ -1362,11 +1363,11 @@ bool QgsComposerMap::readXml( const QDomElement& itemElem, const QDomDocument& d
QString drawCanvasItemsFlag = itemElem.attribute( QStringLiteral( "drawCanvasItems" ), QStringLiteral( "true" ) );
if ( drawCanvasItemsFlag.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
{
mDrawCanvasItems = true;
mDrawAnnotations = true;
}
else
{
mDrawCanvasItems = false;
mDrawAnnotations = false;
}
mLayerStyleOverrides.clear();
@ -1864,37 +1865,31 @@ QPointF QgsComposerMap::mapToItemCoords( QPointF mapCoords ) const
return QPointF( xItem, yItem );
}
void QgsComposerMap::drawCanvasItems( QPainter* painter )
void QgsComposerMap::drawAnnotations( QPainter* painter )
{
if ( !mMapCanvas || !mDrawCanvasItems )
if ( !mComposition || !mComposition->project() || !mDrawAnnotations )
{
return;
}
QList<QGraphicsItem*> itemList = mMapCanvas->items();
if ( itemList.size() < 1 )
{
QList< QgsAnnotation* > annotations = mComposition->project()->annotationManager()->annotations();
if ( annotations.isEmpty() )
return;
}
QGraphicsItem* currentItem = nullptr;
QgsRenderContext rc = QgsComposerUtils::createRenderContextForMap( this, painter );
rc.setForceVectorOutput( true );
rc.setExpressionContext( createExpressionContext() );
for ( int i = itemList.size() - 1; i >= 0; --i )
Q_FOREACH ( QgsAnnotation* annotation, annotations )
{
currentItem = itemList.at( i );
const QgsAnnotation* annotation = dynamic_cast< const QgsAnnotation* >( currentItem );
if ( !annotation )
{
continue;
}
drawCanvasItem( annotation, rc );
drawAnnotation( annotation, rc );
}
}
void QgsComposerMap::drawCanvasItem( const QgsAnnotation* annotation, QgsRenderContext& context )
void QgsComposerMap::drawAnnotation( const QgsAnnotation* annotation, QgsRenderContext& context )
{
if ( !annotation || !annotation->isVisible() || !context.painter() || !context.painter()->device() )
{

View File

@ -352,8 +352,17 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
//! Sets canvas pointer (necessary to query and draw map canvas items)
void setMapCanvas( QGraphicsView* canvas ) { mMapCanvas = canvas; }
void setDrawCanvasItems( bool b ) { mDrawCanvasItems = b; }
bool drawCanvasItems() const { return mDrawCanvasItems; }
/**
* Sets whether annotations are drawn within the composer map.
* @see drawAnnotations()
*/
void setDrawAnnotations( bool draw ) { mDrawAnnotations = draw; }
/**
* Returns whether annotations are drawn within the composer map.
* @see setDrawAnnotations()
*/
bool drawAnnotations() const { return mDrawAnnotations; }
//! Returns the conversion factor map units -> mm
double mapUnitsToMM() const;
@ -571,7 +580,7 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
QRectF mCurrentRectangle;
QGraphicsView* mMapCanvas;
//! True if annotation items, rubber band, etc. from the main canvas should be displayed
bool mDrawCanvasItems;
bool mDrawAnnotations;
/** Adjusts an extent rectangle to match the provided item width and height, so that extent
* center of extent remains the same */
@ -606,8 +615,8 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
@param yShift in: shift in y direction (in item units), out: yShift in map units*/
void transformShift( double& xShift, double& yShift ) const;
void drawCanvasItems( QPainter* painter );
void drawCanvasItem( const QgsAnnotation* item, QgsRenderContext& context );
void drawAnnotations( QPainter* painter );
void drawAnnotation( const QgsAnnotation* item, QgsRenderContext& context );
QPointF composerMapPosForItem( const QgsAnnotation* item ) const;
enum PartType

View File

@ -37,6 +37,7 @@
#include "qgspluginlayerregistry.h"
#include "qgsmessagelog.h"
#include "processing/qgsprocessingregistry.h"
#include "qgsannotationregistry.h"
#include <QDir>
#include <QFile>
@ -135,6 +136,7 @@ QgsApplication::QgsApplication( int & argc, char ** argv, bool GUIenabled, const
mGpsConnectionRegistry = new QgsGPSConnectionRegistry();
mPluginLayerRegistry = new QgsPluginLayerRegistry();
mProcessingRegistry = new QgsProcessingRegistry();
mAnnotationRegistry = new QgsAnnotationRegistry();
init( customConfigPath ); // init can also be called directly by e.g. unit tests that don't inherit QApplication.
}
@ -266,6 +268,7 @@ void QgsApplication::init( QString customConfigPath )
QgsApplication::~QgsApplication()
{
delete mAnnotationRegistry;
delete mProcessingRegistry;
delete mActionScopeRegistry;
delete mTaskManager;
@ -1604,6 +1607,11 @@ QgsProcessingRegistry*QgsApplication::processingRegistry()
return instance()->mProcessingRegistry;
}
QgsAnnotationRegistry*QgsApplication::annotationRegistry()
{
return instance()->mAnnotationRegistry;
}
QgsFieldFormatterRegistry* QgsApplication::fieldFormatterRegistry()
{
return instance()->mFieldFormatterRegistry;

View File

@ -38,6 +38,7 @@ class QgsDataItemProviderRegistry;
class QgsPluginLayerRegistry;
class QgsMessageLog;
class QgsProcessingRegistry;
class QgsAnnotationRegistry;
/** \ingroup core
* Extends QApplication to provide access to QGIS specific resources such
@ -462,6 +463,13 @@ class CORE_EXPORT QgsApplication : public QApplication
*/
static QgsProcessingRegistry* processingRegistry();
/**
* Returns the application's annotation registry, used for managing annotation types.
* @note added in QGIS 3.0
* @note not available in Python bindings
*/
static QgsAnnotationRegistry* annotationRegistry();
#ifdef ANDROID
//dummy method to workaround sip generation issue issue
bool x11EventFilter( XEvent * event )
@ -607,6 +615,7 @@ class CORE_EXPORT QgsApplication : public QApplication
QgsPluginLayerRegistry* mPluginLayerRegistry = nullptr;
QgsMessageLog* mMessageLog = nullptr;
QgsProcessingRegistry* mProcessingRegistry = nullptr;
QgsAnnotationRegistry* mAnnotationRegistry = nullptr;
QString mNullRepresentation;
};

View File

@ -32,6 +32,7 @@
#include "qgsrasterlayer.h"
#include "qgsrectangle.h"
#include "qgsrelationmanager.h"
#include "qgsannotationmanager.h"
#include "qgsvectorlayer.h"
#include "qgsmapthemecollection.h"
#include "qgslayerdefinition.h"
@ -319,6 +320,7 @@ QgsProject::QgsProject( QObject* parent )
, mBadLayerHandler( new QgsProjectBadLayerHandler() )
, mSnappingConfig( this )
, mRelationManager( new QgsRelationManager( this ) )
, mAnnotationManager( new QgsAnnotationManager( this ) )
, mRootGroup( new QgsLayerTreeGroup )
, mAutoTransaction( false )
, mEvaluateDefaultValues( false )
@ -453,6 +455,7 @@ void QgsProject::clear()
mEmbeddedLayers.clear();
mRelationManager->clear();
mAnnotationManager->clear();
mSnappingConfig.reset();
emit snappingConfigChanged();
@ -916,6 +919,8 @@ bool QgsProject::read()
emit mapThemeCollectionChanged();
mMapThemeCollection->readXml( *doc );
mAnnotationManager->readXml( doc->documentElement(), *doc );
// reassign change dependencies now that all layers are loaded
QMap<QString, QgsMapLayer*> existingMaps = mapLayers();
for ( QMap<QString, QgsMapLayer*>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
@ -1284,6 +1289,9 @@ bool QgsProject::write()
mMapThemeCollection->writeXml( *doc );
QDomElement annotationsElem = mAnnotationManager->writeXml( *doc );
qgisNode.appendChild( annotationsElem );
// now wrap it up and ship it to the project file
doc->normalize(); // XXX I'm not entirely sure what this does
@ -2144,6 +2152,11 @@ QgsMapThemeCollection* QgsProject::mapThemeCollection()
return mMapThemeCollection.data();
}
QgsAnnotationManager* QgsProject::annotationManager()
{
return mAnnotationManager.data();
}
void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer*>& layers )
{
QStringList currentLayers = nonIdentifiableLayers();

View File

@ -53,6 +53,7 @@ class QgsRelationManager;
class QgsTolerance;
class QgsTransactionGroup;
class QgsVectorLayer;
class QgsAnnotationManager;
/** \ingroup core
@ -384,6 +385,12 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
*/
QgsMapThemeCollection* mapThemeCollection();
/**
* Returns pointer to the project's annotation manager.
* @note added in QGIS 3.0
*/
QgsAnnotationManager* annotationManager();
/**
* Set a list of layers which should not be taken into account on map identification
*/
@ -960,6 +967,8 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
QgsRelationManager* mRelationManager;
QScopedPointer<QgsAnnotationManager> mAnnotationManager;
QgsLayerTreeGroup* mRootGroup;
QgsLayerTreeRegistryBridge* mLayerTreeRegistryBridge;

View File

@ -43,6 +43,11 @@ class GUI_EXPORT QgsFormAnnotation: public QgsAnnotation
void setAssociatedFeature( const QgsFeature& feature ) override;
/**
* Returns a new QgsFormAnnotation object.
*/
static QgsFormAnnotation* create() { return new QgsFormAnnotation(); }
protected:
void renderAnnotation( QgsRenderContext& context, QSizeF size ) const override;

View File

@ -23,6 +23,8 @@
#include "qgsfeatureiterator.h"
#include "qgscsexception.h"
#include "qgssymbollayerutils.h"
#include "qgsproject.h"
#include "qgsannotationmanager.h"
#include <QPainter>
@ -39,15 +41,14 @@ QgsMapCanvasAnnotationItem::QgsMapCanvasAnnotationItem( QgsAnnotation* annotatio
connect( mMapCanvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
connect( mAnnotation, &QgsAnnotation::mapLayerChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
//lifetime is tied to annotation!
connect( mAnnotation, &QgsAnnotation::destroyed, this, &QgsMapCanvasAnnotationItem::deleteLater );
updatePosition();
setFeatureForMapPosition();
}
QgsMapCanvasAnnotationItem::~QgsMapCanvasAnnotationItem()
{
delete mAnnotation;
}
void QgsMapCanvasAnnotationItem::updatePosition()
{
if ( !mAnnotation )

View File

@ -52,11 +52,9 @@ class GUI_EXPORT QgsMapCanvasAnnotationItem: public QObject, public QgsMapCanvas
};
/**
* Constructor for QgsMapCanvasAnnotationItem. This item takes ownership
* of the annotation.
* Constructor for QgsMapCanvasAnnotationItem.
*/
QgsMapCanvasAnnotationItem( QgsAnnotation* annotation, QgsMapCanvas* mapCanvas );
~QgsMapCanvasAnnotationItem();
/**
* Returns the item's annotation.