mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-02 00:04:53 -04:00
383 lines
9.9 KiB
C++
383 lines
9.9 KiB
C++
/***************************************************************************
|
|
qgslayoutmanager.cpp
|
|
--------------------
|
|
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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "qgslayoutmanager.h"
|
|
#include "qgslayout.h"
|
|
#include "qgsproject.h"
|
|
#include "qgslogger.h"
|
|
#include "qgslayoutundostack.h"
|
|
#include "qgsprintlayout.h"
|
|
#include "qgsreport.h"
|
|
|
|
QgsLayoutManager::QgsLayoutManager( QgsProject *project )
|
|
: QObject( project )
|
|
, mProject( project )
|
|
{
|
|
|
|
}
|
|
|
|
QgsLayoutManager::~QgsLayoutManager()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
bool QgsLayoutManager::addComposition( QgsComposition *composition )
|
|
{
|
|
if ( !composition )
|
|
return false;
|
|
|
|
// check for duplicate name
|
|
Q_FOREACH ( QgsComposition *c, mCompositions )
|
|
{
|
|
if ( c->name() == composition->name() )
|
|
return false;
|
|
}
|
|
|
|
connect( composition, &QgsComposition::nameChanged, this, [this, composition]( const QString & newName )
|
|
{
|
|
emit compositionRenamed( composition, newName );
|
|
} );
|
|
emit compositionAboutToBeAdded( composition->name() );
|
|
mCompositions << composition;
|
|
emit compositionAdded( composition->name() );
|
|
mProject->setDirty( true );
|
|
return true;
|
|
}
|
|
|
|
bool QgsLayoutManager::addLayout( QgsMasterLayoutInterface *layout )
|
|
{
|
|
if ( !layout )
|
|
return false;
|
|
|
|
// check for duplicate name
|
|
for ( QgsMasterLayoutInterface *l : qgis::as_const( mLayouts ) )
|
|
{
|
|
if ( l->name() == layout->name() )
|
|
return false;
|
|
}
|
|
|
|
// ugly, but unavoidable for interfaces...
|
|
if ( QgsPrintLayout *l = dynamic_cast< QgsPrintLayout * >( layout ) )
|
|
{
|
|
connect( l, &QgsPrintLayout::nameChanged, this, [this, l]( const QString & newName )
|
|
{
|
|
emit layoutRenamed( l, newName );
|
|
} );
|
|
}
|
|
else if ( QgsReport *r = dynamic_cast< QgsReport * >( layout ) )
|
|
{
|
|
connect( r, &QgsReport::nameChanged, this, [this, r]( const QString & newName )
|
|
{
|
|
emit layoutRenamed( r, newName );
|
|
} );
|
|
}
|
|
|
|
emit layoutAboutToBeAdded( layout->name() );
|
|
mLayouts << layout;
|
|
emit layoutAdded( layout->name() );
|
|
mProject->setDirty( true );
|
|
return true;
|
|
}
|
|
|
|
bool QgsLayoutManager::removeComposition( QgsComposition *composition )
|
|
{
|
|
if ( !composition )
|
|
return false;
|
|
|
|
if ( !mCompositions.contains( composition ) )
|
|
return false;
|
|
|
|
QString name = composition->name();
|
|
emit compositionAboutToBeRemoved( name );
|
|
mCompositions.removeAll( composition );
|
|
delete composition;
|
|
emit compositionRemoved( name );
|
|
mProject->setDirty( true );
|
|
return true;
|
|
}
|
|
|
|
bool QgsLayoutManager::removeLayout( QgsMasterLayoutInterface *layout )
|
|
{
|
|
if ( !layout )
|
|
return false;
|
|
|
|
if ( !mLayouts.contains( layout ) )
|
|
return false;
|
|
|
|
QString name = layout->name();
|
|
emit layoutAboutToBeRemoved( name );
|
|
mLayouts.removeAll( layout );
|
|
delete layout;
|
|
emit layoutRemoved( name );
|
|
mProject->setDirty( true );
|
|
return true;
|
|
}
|
|
|
|
void QgsLayoutManager::clear()
|
|
{
|
|
Q_FOREACH ( QgsComposition *c, mCompositions )
|
|
{
|
|
removeComposition( c );
|
|
}
|
|
const QList< QgsMasterLayoutInterface * > layouts = mLayouts;
|
|
for ( QgsMasterLayoutInterface *l : layouts )
|
|
{
|
|
removeLayout( l );
|
|
}
|
|
}
|
|
|
|
QList<QgsComposition *> QgsLayoutManager::compositions() const
|
|
{
|
|
return mCompositions;
|
|
}
|
|
|
|
QList<QgsMasterLayoutInterface *> QgsLayoutManager::layouts() const
|
|
{
|
|
return mLayouts;
|
|
}
|
|
|
|
QgsComposition *QgsLayoutManager::compositionByName( const QString &name ) const
|
|
{
|
|
Q_FOREACH ( QgsComposition *c, mCompositions )
|
|
{
|
|
if ( c->name() == name )
|
|
return c;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
QgsMasterLayoutInterface *QgsLayoutManager::layoutByName( const QString &name ) const
|
|
{
|
|
for ( QgsMasterLayoutInterface *l : mLayouts )
|
|
{
|
|
if ( l->name() == name )
|
|
return l;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool QgsLayoutManager::readXml( const QDomElement &element, const QDomDocument &doc )
|
|
{
|
|
clear();
|
|
|
|
QDomElement layoutsElem = element;
|
|
if ( element.tagName() != QStringLiteral( "Layouts" ) )
|
|
{
|
|
layoutsElem = element.firstChildElement( QStringLiteral( "Layouts" ) );
|
|
}
|
|
if ( layoutsElem.isNull() )
|
|
{
|
|
// handle legacy projects
|
|
layoutsElem = doc.documentElement();
|
|
}
|
|
|
|
//restore each composer
|
|
bool result = true;
|
|
QDomNodeList composerNodes = element.elementsByTagName( QStringLiteral( "Composer" ) );
|
|
for ( int i = 0; i < composerNodes.size(); ++i )
|
|
{
|
|
QString legacyTitle = composerNodes.at( i ).toElement().attribute( QStringLiteral( "title" ) );
|
|
QgsComposition *c = createCompositionFromXml( composerNodes.at( i ).toElement(), doc );
|
|
if ( !c )
|
|
{
|
|
result = false;
|
|
continue;
|
|
}
|
|
if ( c->name().isEmpty() )
|
|
c->setName( legacyTitle );
|
|
result = result && addComposition( c );
|
|
}
|
|
|
|
QgsReadWriteContext context;
|
|
context.setPathResolver( mProject->pathResolver() );
|
|
|
|
// restore layouts
|
|
const QDomNodeList layoutNodes = layoutsElem.childNodes();
|
|
for ( int i = 0; i < layoutNodes.size(); ++i )
|
|
{
|
|
if ( layoutNodes.at( i ).nodeName() != QStringLiteral( "Layout" ) )
|
|
continue;
|
|
|
|
std::unique_ptr< QgsPrintLayout > l = qgis::make_unique< QgsPrintLayout >( mProject );
|
|
l->undoStack()->blockCommands( true );
|
|
if ( !l->readLayoutXml( layoutNodes.at( i ).toElement(), doc, context ) )
|
|
{
|
|
result = false;
|
|
continue;
|
|
}
|
|
l->undoStack()->blockCommands( false );
|
|
if ( addLayout( l.get() ) )
|
|
{
|
|
( void )l.release(); // ownership was transferred successfully
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
//reports
|
|
const QDomNodeList reportNodes = element.elementsByTagName( QStringLiteral( "Report" ) );
|
|
for ( int i = 0; i < reportNodes.size(); ++i )
|
|
{
|
|
std::unique_ptr< QgsReport > r = qgis::make_unique< QgsReport >( mProject );
|
|
if ( !r->readLayoutXml( reportNodes.at( i ).toElement(), doc, context ) )
|
|
{
|
|
result = false;
|
|
continue;
|
|
}
|
|
if ( addLayout( r.get() ) )
|
|
{
|
|
( void )r.release(); // ownership was transferred successfully
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QDomElement QgsLayoutManager::writeXml( QDomDocument &doc ) const
|
|
{
|
|
QDomElement layoutsElem = doc.createElement( QStringLiteral( "Layouts" ) );
|
|
Q_FOREACH ( QgsComposition *c, mCompositions )
|
|
{
|
|
QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) );
|
|
|
|
layoutsElem.appendChild( composerElem );
|
|
|
|
c->writeXml( composerElem, doc );
|
|
c->atlasComposition().writeXml( composerElem, doc );
|
|
}
|
|
|
|
QgsReadWriteContext context;
|
|
context.setPathResolver( mProject->pathResolver() );
|
|
for ( QgsMasterLayoutInterface *l : mLayouts )
|
|
{
|
|
QDomElement layoutElem = l->writeLayoutXml( doc, context );
|
|
layoutsElem.appendChild( layoutElem );
|
|
}
|
|
return layoutsElem;
|
|
}
|
|
|
|
bool QgsLayoutManager::saveAsTemplate( const QString &name, QDomDocument &doc ) const
|
|
{
|
|
QgsComposition *c = compositionByName( name );
|
|
if ( !c )
|
|
return false;
|
|
|
|
QDomElement composerElem = doc.createElement( QStringLiteral( "Composer" ) );
|
|
doc.appendChild( composerElem );
|
|
c->writeXml( composerElem, doc );
|
|
c->atlasComposition().writeXml( composerElem, doc );
|
|
return true;
|
|
}
|
|
|
|
QgsMasterLayoutInterface *QgsLayoutManager::duplicateLayout( const QgsMasterLayoutInterface *layout, const QString &newName )
|
|
{
|
|
if ( !layout )
|
|
return nullptr;
|
|
|
|
std::unique_ptr< QgsMasterLayoutInterface > newLayout( layout->clone() );
|
|
if ( !newLayout )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
newLayout->setName( newName );
|
|
QgsMasterLayoutInterface *l = newLayout.get();
|
|
if ( !addLayout( l ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
( void )newLayout.release(); //ownership was transferred successfully
|
|
return l;
|
|
}
|
|
}
|
|
|
|
QString QgsLayoutManager::generateUniqueComposerTitle() const
|
|
{
|
|
QStringList names;
|
|
Q_FOREACH ( QgsComposition *c, mCompositions )
|
|
{
|
|
names << c->name();
|
|
}
|
|
QString name;
|
|
int id = 1;
|
|
while ( name.isEmpty() || names.contains( name ) )
|
|
{
|
|
name = tr( "Composer %1" ).arg( id );
|
|
id++;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
QString QgsLayoutManager::generateUniqueTitle( QgsMasterLayoutInterface::Type type ) const
|
|
{
|
|
QStringList names;
|
|
for ( QgsMasterLayoutInterface *l : mLayouts )
|
|
{
|
|
names << l->name();
|
|
}
|
|
QString name;
|
|
int id = 1;
|
|
while ( name.isEmpty() || names.contains( name ) )
|
|
{
|
|
switch ( type )
|
|
{
|
|
case QgsMasterLayoutInterface::PrintLayout:
|
|
name = tr( "Layout %1" ).arg( id );
|
|
break;
|
|
case QgsMasterLayoutInterface::Report:
|
|
name = tr( "Report %1" ).arg( id );
|
|
break;
|
|
}
|
|
id++;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
QgsComposition *QgsLayoutManager::createCompositionFromXml( const QDomElement &element, const QDomDocument &doc ) const
|
|
{
|
|
QDomNodeList compositionNodeList = element.elementsByTagName( QStringLiteral( "Composition" ) );
|
|
|
|
if ( compositionNodeList.size() > 0 )
|
|
{
|
|
std::unique_ptr< QgsComposition > c( new QgsComposition( mProject ) );
|
|
QDomElement compositionElem = compositionNodeList.at( 0 ).toElement();
|
|
if ( !c->readXml( compositionElem, doc ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// read atlas parameters - must be done before adding items
|
|
QDomElement atlasElem = element.firstChildElement( QStringLiteral( "Atlas" ) );
|
|
c->atlasComposition().readXml( atlasElem, doc );
|
|
|
|
//read and restore all the items
|
|
c->addItemsFromXml( element, doc );
|
|
|
|
//make sure z values are consistent
|
|
c->refreshZList();
|
|
return c.release();
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|