diff --git a/python/core/layout/qgslayout.sip b/python/core/layout/qgslayout.sip index 9d4678b48fa..9a8ea9d8a06 100644 --- a/python/core/layout/qgslayout.sip +++ b/python/core/layout/qgslayout.sip @@ -56,6 +56,12 @@ called on the new layout. ~QgsLayout(); + virtual QgsLayout *clone() const /Factory/; +%Docstring +Creates a clone of the layout. Ownership of the return layout +is transferred to the caller. +%End + void initializeDefaults(); %Docstring Initializes an empty layout, e.g. by adding a default page to the layout. This should be called after creating diff --git a/python/core/layout/qgsprintlayout.sip b/python/core/layout/qgsprintlayout.sip index 474fd4d2345..26faa334e32 100644 --- a/python/core/layout/qgsprintlayout.sip +++ b/python/core/layout/qgsprintlayout.sip @@ -26,6 +26,9 @@ class QgsPrintLayout : QgsLayout Constructor for QgsPrintLayout. %End + virtual QgsPrintLayout *clone() const /Factory/; + + QgsLayoutAtlas *atlas(); %Docstring Returns the print layout's atlas. diff --git a/src/core/composer/qgslayoutmanager.cpp b/src/core/composer/qgslayoutmanager.cpp index 333794a7d75..77550bd051c 100644 --- a/src/core/composer/qgslayoutmanager.cpp +++ b/src/core/composer/qgslayoutmanager.cpp @@ -283,16 +283,8 @@ QgsComposition *QgsLayoutManager::duplicateComposition( const QString &name, con QgsLayout *QgsLayoutManager::duplicateLayout( const QgsLayout *layout, const QString &newName ) { - QDomDocument currentDoc; - - QgsReadWriteContext context; - QDomElement elem = layout->writeXml( currentDoc, context ); - currentDoc.appendChild( elem ); - - std::unique_ptr< QgsLayout > newLayout = qgis::make_unique< QgsPrintLayout >( mProject ); - bool ok = false; - newLayout->loadFromTemplate( currentDoc, context, true, &ok ); - if ( !ok ) + std::unique_ptr< QgsLayout > newLayout( layout->clone() ); + if ( !newLayout ) { return nullptr; } diff --git a/src/core/layout/qgslayout.cpp b/src/core/layout/qgslayout.cpp index bab4c0b5ad1..dd8608917e7 100644 --- a/src/core/layout/qgslayout.cpp +++ b/src/core/layout/qgslayout.cpp @@ -75,6 +75,25 @@ QgsLayout::~QgsLayout() mItemsModel.reset(); // manually delete, so we can control order of destruction } +QgsLayout *QgsLayout::clone() const +{ + QDomDocument currentDoc; + + QgsReadWriteContext context; + QDomElement elem = writeXml( currentDoc, context ); + currentDoc.appendChild( elem ); + + std::unique_ptr< QgsLayout > newLayout = qgis::make_unique< QgsLayout >( mProject ); + bool ok = false; + newLayout->loadFromTemplate( currentDoc, context, true, &ok ); + if ( !ok ) + { + return nullptr; + } + + return newLayout.release(); +} + void QgsLayout::initializeDefaults() { // default to a A4 landscape page diff --git a/src/core/layout/qgslayout.h b/src/core/layout/qgslayout.h index 58589fd360a..8e196d5c0d1 100644 --- a/src/core/layout/qgslayout.h +++ b/src/core/layout/qgslayout.h @@ -83,6 +83,12 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext ~QgsLayout() override; + /** + * Creates a clone of the layout. Ownership of the return layout + * is transferred to the caller. + */ + virtual QgsLayout *clone() const SIP_FACTORY; + /** * Initializes an empty layout, e.g. by adding a default page to the layout. This should be called after creating * a new layout. diff --git a/src/core/layout/qgsprintlayout.cpp b/src/core/layout/qgsprintlayout.cpp index 72afbbc8011..9db82592b35 100644 --- a/src/core/layout/qgsprintlayout.cpp +++ b/src/core/layout/qgsprintlayout.cpp @@ -16,6 +16,7 @@ #include "qgsprintlayout.h" #include "qgslayoutatlas.h" +#include "qgsreadwritecontext.h" QgsPrintLayout::QgsPrintLayout( QgsProject *project ) : QgsLayout( project ) @@ -23,6 +24,25 @@ QgsPrintLayout::QgsPrintLayout( QgsProject *project ) { } +QgsPrintLayout *QgsPrintLayout::clone() const +{ + QDomDocument currentDoc; + + QgsReadWriteContext context; + QDomElement elem = writeXml( currentDoc, context ); + currentDoc.appendChild( elem ); + + std::unique_ptr< QgsPrintLayout > newLayout = qgis::make_unique< QgsPrintLayout >( project() ); + bool ok = false; + newLayout->loadFromTemplate( currentDoc, context, true, &ok ); + if ( !ok ) + { + return nullptr; + } + + return newLayout.release(); +} + QgsLayoutAtlas *QgsPrintLayout::atlas() { return mAtlas; diff --git a/src/core/layout/qgsprintlayout.h b/src/core/layout/qgsprintlayout.h index 69817528afd..b0471fad98e 100644 --- a/src/core/layout/qgsprintlayout.h +++ b/src/core/layout/qgsprintlayout.h @@ -38,6 +38,8 @@ class CORE_EXPORT QgsPrintLayout : public QgsLayout */ QgsPrintLayout( QgsProject *project ); + QgsPrintLayout *clone() const override SIP_FACTORY; + /** * Returns the print layout's atlas. */ diff --git a/tests/src/core/testqgslayout.cpp b/tests/src/core/testqgslayout.cpp index 37e5d57b2be..329d74653a0 100644 --- a/tests/src/core/testqgslayout.cpp +++ b/tests/src/core/testqgslayout.cpp @@ -26,6 +26,8 @@ #include "qgslayoutitempolyline.h" #include "qgslayoutitemhtml.h" #include "qgslayoutframe.h" +#include "qgsprintlayout.h" +#include "qgslayoutatlas.h" class TestQgsLayout: public QObject { @@ -54,6 +56,7 @@ class TestQgsLayout: public QObject void pageIsEmpty(); void clear(); void georeference(); + void clone(); private: QString mReport; @@ -844,6 +847,44 @@ void TestQgsLayout::georeference() t.reset(); } +void TestQgsLayout::clone() +{ + QgsProject proj; + QgsLayout l( &proj ); + QgsLayoutItemPage *page = new QgsLayoutItemPage( &l ); + page->setPageSize( "A4" ); + l.pageCollection()->addPage( page ); + QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l ); + page2->setPageSize( "A4" ); + l.pageCollection()->addPage( page2 ); + QgsLayoutItemPage *page3 = new QgsLayoutItemPage( &l ); + page3->setPageSize( "A4" ); + l.pageCollection()->addPage( page3 ); + + //add some items to the composition + QgsLayoutItemShape *label1 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label1 ); + QgsLayoutItemShape *label2 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label2 ); + QgsLayoutItemShape *label3 = new QgsLayoutItemShape( &l ); + l.addLayoutItem( label3 ); + + // clone and check a few poperties + std::unique_ptr< QgsLayout > cloned( l.clone() ); + QVERIFY( cloned.get() ); + QCOMPARE( cloned->pageCollection()->pageCount(), 3 ); + QList< QgsLayoutItem * > items; + cloned->layoutItems( items ); + QCOMPARE( items.count(), 6 ); // 3 pages + 3 items + + // clone a print layout + QgsPrintLayout pl( &proj ); + pl.atlas()->setPageNameExpression( QStringLiteral( "not a real expression" ) ); + std::unique_ptr< QgsPrintLayout > plClone( pl.clone() ); + QVERIFY( plClone.get() ); + QCOMPARE( plClone->atlas()->pageNameExpression(), QStringLiteral( "not a real expression" ) ); +} + QGSTEST_MAIN( TestQgsLayout ) #include "testqgslayout.moc"