Skip to content

Commit

Permalink
Save/restore frame properties, add unit tests for undo/redo
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Oct 6, 2017
1 parent 616aec1 commit f3bfcc3
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/core/layout/qgslayoutitem.cpp
Expand Up @@ -20,6 +20,7 @@
#include "qgspagesizeregistry.h"
#include "qgslayoutitemundocommand.h"
#include "qgslayoutmodel.h"
#include "qgssymbollayerutils.h"
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QUuid>
Expand Down Expand Up @@ -661,6 +662,44 @@ bool QgsLayoutItem::writePropertiesToElement( QDomElement &element, QDomDocument
element.setAttribute( "positionLock", "false" );
}

//frame
if ( mFrame )
{
element.setAttribute( QStringLiteral( "frame" ), QStringLiteral( "true" ) );
}
else
{
element.setAttribute( QStringLiteral( "frame" ), QStringLiteral( "false" ) );
}

//background
if ( mBackground )
{
element.setAttribute( QStringLiteral( "background" ), QStringLiteral( "true" ) );
}
else
{
element.setAttribute( QStringLiteral( "background" ), QStringLiteral( "false" ) );
}

//frame color
QDomElement frameColorElem = document.createElement( QStringLiteral( "FrameColor" ) );
frameColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mFrameColor.red() ) );
frameColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mFrameColor.green() ) );
frameColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mFrameColor.blue() ) );
frameColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mFrameColor.alpha() ) );
element.appendChild( frameColorElem );
element.setAttribute( QStringLiteral( "outlineWidthM" ), mFrameWidth.encodeMeasurement() );
element.setAttribute( QStringLiteral( "frameJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mFrameJoinStyle ) );

//background color
QDomElement bgColorElem = document.createElement( QStringLiteral( "BackgroundColor" ) );
bgColorElem.setAttribute( QStringLiteral( "red" ), QString::number( mBackgroundColor.red() ) );
bgColorElem.setAttribute( QStringLiteral( "green" ), QString::number( mBackgroundColor.green() ) );
bgColorElem.setAttribute( QStringLiteral( "blue" ), QString::number( mBackgroundColor.blue() ) );
bgColorElem.setAttribute( QStringLiteral( "alpha" ), QString::number( mBackgroundColor.alpha() ) );
element.appendChild( bgColorElem );

//TODO
#if 0
//blend mode
Expand Down Expand Up @@ -706,6 +745,78 @@ bool QgsLayoutItem::readPropertiesFromElement( const QDomElement &element, const
//visibility
setVisible( element.attribute( "visibility", "1" ) != "0" );
setZValue( element.attribute( "zValue" ).toDouble() );

//frame
QString frame = element.attribute( QStringLiteral( "frame" ) );
if ( frame.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
{
mFrame = true;
}
else
{
mFrame = false;
}

//frame
QString background = element.attribute( QStringLiteral( "background" ) );
if ( background.compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0 )
{
mBackground = true;
}
else
{
mBackground = false;
}

//pen
mFrameWidth = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "outlineWidthM" ) ) );
mFrameJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( element.attribute( QStringLiteral( "frameJoinStyle" ), QStringLiteral( "miter" ) ) );
QDomNodeList frameColorList = element.elementsByTagName( QStringLiteral( "FrameColor" ) );
if ( !frameColorList.isEmpty() )
{
QDomElement frameColorElem = frameColorList.at( 0 ).toElement();
bool redOk = false;
bool greenOk = false;
bool blueOk = false;
bool alphaOk = false;
int penRed, penGreen, penBlue, penAlpha;

#if 0 // TODO, old style
double penWidth;
penWidth = element.attribute( QStringLiteral( "outlineWidth" ) ).toDouble( &widthOk );
#endif
penRed = frameColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
penGreen = frameColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
penBlue = frameColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
penAlpha = frameColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );

if ( redOk && greenOk && blueOk && alphaOk )
{
mFrameColor = QColor( penRed, penGreen, penBlue, penAlpha );
}
}
refreshFrame( false );

//brush
QDomNodeList bgColorList = element.elementsByTagName( QStringLiteral( "BackgroundColor" ) );
if ( !bgColorList.isEmpty() )
{
QDomElement bgColorElem = bgColorList.at( 0 ).toElement();
bool redOk, greenOk, blueOk, alphaOk;
int bgRed, bgGreen, bgBlue, bgAlpha;
bgRed = bgColorElem.attribute( QStringLiteral( "red" ) ).toDouble( &redOk );
bgGreen = bgColorElem.attribute( QStringLiteral( "green" ) ).toDouble( &greenOk );
bgBlue = bgColorElem.attribute( QStringLiteral( "blue" ) ).toDouble( &blueOk );
bgAlpha = bgColorElem.attribute( QStringLiteral( "alpha" ) ).toDouble( &alphaOk );
if ( redOk && greenOk && blueOk && alphaOk )
{
mBackgroundColor = QColor( bgRed, bgGreen, bgBlue, bgAlpha );
setBrush( QBrush( mBackgroundColor, Qt::SolidPattern ) );
}
//apply any data defined settings
refreshBackgroundColor( false );
}

#if 0 //TODO
//blend mode
setBlendMode( QgsMapRenderer::getCompositionMode( ( QgsMapRenderer::BlendMode ) itemElem.attribute( "blendMode", "0" ).toUInt() ) );
Expand Down Expand Up @@ -794,6 +905,7 @@ void QgsLayoutItem::refreshFrame( bool updateItem )
{
itemPen.setColor( mFrameColor );
}
itemPen.setJoinStyle( mFrameJoinStyle );

if ( mLayout )
itemPen.setWidthF( mLayout->convertToLayoutUnits( mFrameWidth ) );
Expand Down
110 changes: 110 additions & 0 deletions tests/src/core/testqgslayoutitem.cpp
Expand Up @@ -22,6 +22,9 @@
#include "qgstest.h"
#include "qgsproject.h"
#include "qgsreadwritecontext.h"
#include "qgslayoutitemundocommand.h"
#include "qgslayoutitemmap.h"
#include "qgslayoutitemshape.h"
#include <QObject>
#include <QPainter>
#include <QImage>
Expand Down Expand Up @@ -144,6 +147,7 @@ class TestQgsLayoutItem: public QObject
void writeXml();
void readXml();
void writeReadXmlProperties();
void undoRedo();

private:

Expand Down Expand Up @@ -1324,6 +1328,12 @@ void TestQgsLayoutItem::writeReadXmlProperties()
original->setLocked( true );
original->setZValue( 55 );
original->setVisible( false );
original->setFrameEnabled( true );
original->setFrameStrokeColor( QColor( 100, 150, 200 ) );
original->setFrameStrokeWidth( QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) );
original->setFrameJoinStyle( Qt::MiterJoin );
original->setBackgroundEnabled( false );
original->setBackgroundColor( QColor( 200, 150, 100 ) );

QgsLayoutItem *copy = createCopyViaXml( &l, original );

Expand All @@ -1340,11 +1350,111 @@ void TestQgsLayoutItem::writeReadXmlProperties()
QVERIFY( copy->isLocked() );
QCOMPARE( copy->zValue(), 55.0 );
QVERIFY( !copy->isVisible() );
QVERIFY( copy->hasFrame() );
QCOMPARE( copy->frameStrokeColor(), QColor( 100, 150, 200 ) );
QCOMPARE( copy->frameStrokeWidth(), QgsLayoutMeasurement( 5, QgsUnitTypes::LayoutCentimeters ) );
QCOMPARE( copy->frameJoinStyle(), Qt::MiterJoin );
QVERIFY( !copy->hasBackground() );
QCOMPARE( copy->backgroundColor(), QColor( 200, 150, 100 ) );

delete copy;
delete original;
}

void TestQgsLayoutItem::undoRedo()
{
QgsProject proj;
QgsLayout l( &proj );

QgsLayoutItemRectangularShape *item = new QgsLayoutItemRectangularShape( &l );
QString uuid = item->uuid();
QPointer< QgsLayoutItemRectangularShape > pItem( item ); // for testing deletion
item->setFrameStrokeColor( QColor( 255, 100, 200 ) );
l.addLayoutItem( item );

l.undoStack()->stack()->push( new QgsLayoutItemAddItemCommand( item, QString() ) );
QVERIFY( pItem );
QVERIFY( l.items().contains( item ) );
QCOMPARE( l.itemByUuid( uuid ), item );

// undo should delete item
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );

// redo should restore
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemRectangularShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );

//... and repeat!

// undo should delete item
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );

// redo should restore
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemRectangularShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );

// delete item
QgsLayoutItemDeleteUndoCommand *deleteCommand = new QgsLayoutItemDeleteUndoCommand( item, QString() );
l.removeLayoutItem( item );
l.undoStack()->stack()->push( deleteCommand );

QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );

// undo should restore
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemRectangularShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );

// another undo should delete item
l.undoStack()->stack()->undo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );

// redo should restore
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
item = dynamic_cast< QgsLayoutItemRectangularShape * >( l.itemByUuid( uuid ) );
QVERIFY( item );
QVERIFY( l.items().contains( item ) );
pItem = item;
QCOMPARE( item->frameStrokeColor().name(), QColor( 255, 100, 200 ).name() );

// another redo should delete item
l.undoStack()->stack()->redo();
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( !pItem );
QVERIFY( !l.items().contains( item ) );
QVERIFY( !l.itemByUuid( uuid ) );

}

QgsLayoutItem *TestQgsLayoutItem::createCopyViaXml( QgsLayout *layout, QgsLayoutItem *original )
{
//save original item to xml
Expand Down

0 comments on commit f3bfcc3

Please sign in to comment.